簡體   English   中英

如何使用 OpenXml 2.0 將任何文件類型嵌入到 Microsoft Word 中

[英]How can I embed any file type into Microsoft Word using OpenXml 2.0

我花了很多時間試圖找出一種使用 OpenXml 2.0 將任何文件嵌入到 Microsoft Word 中的好方法; Office 文檔相當簡單,但其他文件類型如 PDF、TXT、GIF、JPG、HTML 等呢?...

在 C# 中,讓它適用於任何文件類型的好方法是什么?

使用 OpenXml 2.0(嗯,與 COM 合作)將外來對象(PDF、TXT、GIF 等)嵌入到 Microsoft Word 中

我從這個網站上得到了很多,所以在這里我提出並回答了我自己的問題,以便對我難以找到答案的主題有所回饋,希望它可以幫助人們。

有幾個示例展示了如何使用 OpenXml 2.0 將 Office 文檔嵌入到另一個 Office 文檔中,但不存在且易於理解的是如何將幾乎任何文件嵌入到 Office 文檔中。

我從其他人的代碼中學到了很多東西,所以這是我的貢獻。 由於我已經在使用 OpenXml 生成文檔,並且我需要將其他文件嵌入到 Word 中,因此我決定使用 OpenXml 和 COM(Microsoft Office 2007 dll)的協作來實現我的目標。 如果您像我一樣,“調用 OLE 服務器應用程序來創建 IStorage”對您來說意義不大。

在這個例子中,我想展示我如何使用 COM 以編程方式獲取附件的 OLE 二進制數據信息,然后我如何在我的 OpenXml 文檔中使用該信息。 基本上,我以編程方式查看 OpenXml 2.0 文檔反射器以獲取我需要的信息。

我下面的代碼分為幾個類,但這里是我在做什么的大綱:

  1. 創建一個 OpenXml WordProcessingDocument,獲取要嵌入的文件的 System.IO.FileInfo
  2. 創建一個自定義 OpenXmlEmbeddedObject 對象(這是保存所有二進制數據的內容)
  3. 使用上述步驟中的二進制數據創建數據和圖像流
  4. 將這些流用作 OpenXml 文檔的文件對象和文件圖像

我知道有很多代碼,並沒有太多解釋......希望它很容易理解並且可以幫助人們 

要求: • DocumentFormat.OpenXml dll (OpenXml 2.0) • WindowsBase dll • Microsoft.Office.Interop.Word dll(Office 2007 – 版本 12)

• 這是啟動一切的主類,打開 WordProcessingDocument 和類以附加文件

using DocumentFormat.OpenXml.Packaging;
using System.IO;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Wordprocessing;

public class MyReport
{
    private MainDocumentPart _mainDocumentPart;

    public void CreateReport()
    {
        using (WordprocessingDocument wpDocument = WordprocessingDocument.Create(@"TempPath\MyReport.docx", WordprocessingDocumentType.Document))
        {
            _mainDocumentPart = wpDocument.AddMainDocumentPart();
            _mainDocumentPart.Document = new Document(new Body());

            AttachFile(@"MyFilePath\MyFile.pdf", true);
        }
    }

    private void AttachFile(string filePathAndName, bool displayAsIcon)
    {
        FileInfo fileInfo = new FileInfo(filePathAndName);

        OpenXmlHelper.AppendEmbeddedObject(_mainDocumentPart, fileInfo, displayAsIcon);
    }
}

• OpenXml 助手類中的此類,包含將對象嵌入 OpenXml 文件的所有邏輯

using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Validation;
using DocumentFormat.OpenXml.Wordprocessing;
using OVML = DocumentFormat.OpenXml.Vml.Office;
using V = DocumentFormat.OpenXml.Vml;

public class OpenXmlHelper
{
    /// <summary>
    /// Appends an Embedded Object into the specified Main Document
    /// </summary>
    /// <param name="mainDocumentPart">The MainDocument Part of your OpenXml Word Doc</param>
    /// <param name="fileInfo">The FileInfo object associated with the file being embedded</param>
    /// <param name="displayAsIcon">Whether or not to display the embedded file as an Icon (Otherwise it will display a snapshot of the file)</param>
    public static void AppendEmbeddedObject(MainDocumentPart mainDocumentPart, FileInfo fileInfo, bool displayAsIcon)
    {
        OpenXmlEmbeddedObject openXmlEmbeddedObject = new OpenXmlEmbeddedObject(fileInfo, displayAsIcon);

        if (!String.IsNullOrEmpty(openXmlEmbeddedObject.OleObjectBinaryData))
        {
            using (Stream dataStream = new MemoryStream(Convert.FromBase64String(openXmlEmbeddedObject.OleObjectBinaryData)))
            {
                if (!String.IsNullOrEmpty(openXmlEmbeddedObject.OleImageBinaryData))
                {
                    using (Stream emfStream = new MemoryStream(Convert.FromBase64String(openXmlEmbeddedObject.OleImageBinaryData)))
                    {
                        string imagePartId = GetUniqueXmlItemID();
                        ImagePart imagePart = mainDocumentPart.AddImagePart(ImagePartType.Emf, imagePartId);

                        if (emfStream != null)
                        {
                            imagePart.FeedData(emfStream);
                        }

                        string embeddedPackagePartId = GetUniqueXmlItemID();

                        if (dataStream != null)
                        {
                            if (openXmlEmbeddedObject.ObjectIsOfficeDocument)
                            {
                                EmbeddedPackagePart embeddedObjectPart = mainDocumentPart.AddNewPart<EmbeddedPackagePart>(
                                    openXmlEmbeddedObject.FileContentType, embeddedPackagePartId);
                                embeddedObjectPart.FeedData(dataStream);
                            }
                            else
                            {
                                EmbeddedObjectPart embeddedObjectPart = mainDocumentPart.AddNewPart<EmbeddedObjectPart>(
                                    openXmlEmbeddedObject.FileContentType, embeddedPackagePartId);
                                embeddedObjectPart.FeedData(dataStream);
                            }
                        }

                        if (!displayAsIcon && !openXmlEmbeddedObject.ObjectIsPicture)
                        {
                            Paragraph attachmentHeader = CreateParagraph(String.Format("Attachment: {0} (Double-Click to Open)", fileInfo.Name));
                            mainDocumentPart.Document.Body.Append(attachmentHeader);
                        }

                        Paragraph embeddedObjectParagraph = GetEmbeededObjectParagraph(openXmlEmbeddedObject.FileType,
                            imagePartId, openXmlEmbeddedObject.OleImageStyle, embeddedPackagePartId);

                        mainDocumentPart.Document.Body.Append(embeddedObjectParagraph);
                    }
                }
            }
        }
    }

    /// <summary>
    /// Gets Paragraph that includes the embedded object
    /// </summary>
    private static Paragraph GetEmbeededObjectParagraph(string fileType, string imageID, string imageStyle, string embeddedPackageID)
    {
        EmbeddedObject embeddedObject = new EmbeddedObject();

        string shapeID = GetUniqueXmlItemID();
        V.Shape shape = new V.Shape() { Id = shapeID, Style = imageStyle };
        V.ImageData imageData = new V.ImageData() { Title = "", RelationshipId = imageID };

        shape.Append(imageData);
        OVML.OleObject oleObject = new OVML.OleObject()
        {
            Type = OVML.OleValues.Embed,
            ProgId = fileType,
            ShapeId = shapeID,
            DrawAspect = OVML.OleDrawAspectValues.Icon,
            ObjectId = GetUniqueXmlItemID(),
            Id = embeddedPackageID
        };

        embeddedObject.Append(shape);
        embeddedObject.Append(oleObject);

        Paragraph paragraphImage = new Paragraph();

        Run runImage = new Run(embeddedObject);
        paragraphImage.Append(runImage);

        return paragraphImage;
    }

    /// <summary>
    /// Gets a Unique ID for an XML Item, for reference purposes
    /// </summary>
    /// <returns>A GUID string with removed dashes</returns>
    public static string GetUniqueXmlItemID()
    {
        return "r" + System.Guid.NewGuid().ToString().Replace("-", "");
    }

    private static Paragraph CreateParagraph(string paragraphText)
    {
        Paragraph paragraph = new Paragraph();
        ParagraphProperties paragraphProperties = new ParagraphProperties();

        paragraphProperties.Append(new Justification()
        {
            Val = JustificationValues.Left
        });

        paragraphProperties.Append(new SpacingBetweenLines()
        {
            After = Convert.ToString(100),
            Line = Convert.ToString(100),
            LineRule = LineSpacingRuleValues.AtLeast
        });

        Run run = new Run();
        RunProperties runProperties = new RunProperties();

        Text text = new Text();

        if (!String.IsNullOrEmpty(paragraphText))
        {
            text.Text = paragraphText;
        }

        run.Append(runProperties);
        run.Append(text);

        paragraph.Append(paragraphProperties);
        paragraph.Append(run);

        return paragraph;
    }

}

• 這是此過程中最重要的部分,它使用 Microsoft 的內部 OLE 服務器,為文件創建二進制數據和二進制 EMF 信息。 您所要做的就是調用 OpenXmlEmbeddedObject 構造函數,一切都將得到處理。 它將模仿您手動將任何文件拖入 Word 時的過程; 執行此操作時會進行某種轉換,將文件轉換為 OLE 對象,以便 Microsoft 可以識別該文件。 o 這個類中最重要的部分是 OleObjectBinaryData 和 OleImageBinaryData 屬性; 它們包含文件數據和“.emf”圖像的 64 位字符串二進制信息。 o 如果您選擇不將文件顯示為圖標,則 '.emf' 圖像數據將創建文件的快照,例如 pdf 文件的第一頁,您仍然可以在其中雙擊打開o 如果您要嵌入圖像並選擇不將其顯示為圖標,則 OleObjectBinaryData 和 OleImageBinaryData 屬性將相同

using System.Runtime.InteropServices;
using System.Xml;
using System.Diagnostics;
using System.IO;
using System.Drawing;
using Microsoft.Office.Interop.Word;

public class OpenXmlEmbeddedObject
{
    #region Constants

    private const string _defaultOleContentType = "application/vnd.openxmlformats-officedocument.oleObject";
    private const string _oleObjectDataTag = "application/vnd";
    private const string _oleImageDataTag = "image/x-emf";

    #endregion Constants

    #region Member Variables

    private static FileInfo _fileInfo;
    private static string _filePathAndName;
    private static bool _displayAsIcon;
    private static bool _objectIsPicture;

    private object _objectMissing = System.Reflection.Missing.Value;
    private object _objectFalse = false;
    private object _objectTrue = true;

    #endregion Member Variables

    #region Properties

    /// <summary>
    /// The File Type, as stored in Registry (Ex: a GIF Image = 'giffile')
    /// </summary>
    public string FileType
    {
        get
        {
            if (String.IsNullOrEmpty(_fileType) && _fileInfo != null)
            {
                _fileType = GetFileType(_fileInfo, false);
            }

            return _fileType;
        }
    }
    private string _fileType;

    /// <summary>
    /// The File Context Type, as storered in Registry (Ex: a GIF Image = 'image/gif')
    /// * Is converted into the 'Default Office Context Type' for non-office files
    /// </summary>
    public string FileContentType
    {
        get
        {
            if (String.IsNullOrEmpty(_fileContentType) && _fileInfo != null)
            {
                _fileContentType = GetFileContentType(_fileInfo);

                if (!_fileContentType.Contains("officedocument"))
                {
                    _fileContentType = _defaultOleContentType;
                }
            }

            return _fileContentType;
        }
    }
    private string _fileContentType;

    /// <summary>
    /// Gets the ContentType Text for the file
    /// </summary>
    public static string GetFileContentType(FileInfo fileInfo)
    {
        if (fileInfo == null)
        {
            throw new ArgumentNullException("fileInfo");
        }

        string mime = "application/octetstream";

        string ext = System.IO.Path.GetExtension(fileInfo.Name).ToLower(); 

        Microsoft.Win32.RegistryKey rk = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(ext);

        if (rk != null && rk.GetValue("Content Type") != null)
        {
            mime = rk.GetValue("Content Type").ToString();
        }

        return mime;
    }

    public bool ObjectIsOfficeDocument
    {
        get { return FileContentType != _defaultOleContentType; }
    }

    public bool ObjectIsPicture
    {
        get { return _objectIsPicture; }
    }

    public string OleObjectBinaryData
    {
        get { return _oleObjectBinaryData; }
        set { _oleObjectBinaryData = value; }
    }
    private string _oleObjectBinaryData;

    public string OleImageBinaryData
    {
        get { return _oleImageBinaryData; }
        set { _oleImageBinaryData = value; }
    }
    private string _oleImageBinaryData;

    /// <summary>
    /// The OpenXml information for the Word Application that is created (Make-Shoft Code Reflector)
    /// </summary>
    public string WordOpenXml
    {
        get { return _wordOpenXml; }
        set { _wordOpenXml = value; }
    }
    private String _wordOpenXml;

    /// <summary>
    /// The XmlDocument that is created based on the OpenXml Data from WordOpenXml
    /// </summary>
    public XmlDocument OpenXmlDocument
    {
        get
        {
            if (_openXmlDocument == null && !String.IsNullOrEmpty(WordOpenXml))
            {
                _openXmlDocument = new XmlDocument();
                _openXmlDocument.LoadXml(WordOpenXml);
            }

            return _openXmlDocument;
        }
    }
    private XmlDocument _openXmlDocument;

    /// <summary>
    /// The XmlNodeList, for all Nodes containing 'binaryData'
    /// </summary>
    public XmlNodeList BinaryDataXmlNodesList
    {
        get
        {
            if (_binaryDataXmlNodesList == null && OpenXmlDocument != null)
            {
                _binaryDataXmlNodesList = OpenXmlDocument.GetElementsByTagName("pkg:binaryData");
            }

            return _binaryDataXmlNodesList;
        }
    }
    private XmlNodeList _binaryDataXmlNodesList;

    /// <summary>
    /// Icon Object for the file
    /// </summary>
    public Icon ObjectIcon
    {
        get
        {
            if (_objectIcon == null)
            {
                _objectIcon = Enterprise.Windows.Win32.Win32.GetLargeIcon(_filePathAndName);
            }

            return _objectIcon;
        }
    }
    private Icon _objectIcon;

    /// <summary>
    /// File Name for the Icon being created
    /// </summary>
    public string ObjectIconFile
    {
        get
        {
            if (String.IsNullOrEmpty(_objectIconFile))
            {
                _objectIconFile = String.Format("{0}.ico", _filePathAndName.Replace(".", ""));
            }

            return _objectIconFile;
        }
    }
    private string _objectIconFile;

    /// <summary>
    /// Gets the original height and width of the emf file being created
    /// </summary>
    public string OleImageStyle
    {
        get
        {
            if (String.IsNullOrEmpty(_oleImageStyle) && !String.IsNullOrEmpty(WordOpenXml))
            {
                XmlNodeList xmlNodeList = OpenXmlDocument.GetElementsByTagName("v:shape");
                if (xmlNodeList != null && xmlNodeList.Count > 0)
                {
                    foreach (XmlAttribute attribute in xmlNodeList[0].Attributes)
                    {
                        if (attribute.Name == "style")
                        {
                            _oleImageStyle = attribute.Value;
                        }
                    }
                }
            }

            return _oleImageStyle;
        }

        set { _oleImageStyle = value; }
    }
    private string _oleImageStyle;

    #endregion Properties

    #region Constructor

    /// <summary>
    /// Generates binary information for the file being passed in
    /// </summary>
    /// <param name="fileInfo">The FileInfo object for the file to be embedded</param>
    /// <param name="displayAsIcon">Whether or not to display the file as an Icon (Otherwise it will show a snapshot view of the file)</param>
    public OpenXmlEmbeddedObject(FileInfo fileInfo, bool displayAsIcon)
    {
        _fileInfo = fileInfo;
        _filePathAndName = fileInfo.ToString();
        _displayAsIcon = displayAsIcon;

        SetupOleFileInformation();
    }

    #endregion Constructor

    #region Methods

    /// <summary>
    /// Creates a temporary Word App in order to add an OLE Object, get's the OpenXML data from the file (similar to the Code Reflector info)
    /// </summary>
    private void SetupOleFileInformation()
    {
        Microsoft.Office.Interop.Word.Application wordApplication = new Microsoft.Office.Interop.Word.Application();

        Microsoft.Office.Interop.Word.Document wordDocument = wordApplication.Documents.Add(ref _objectMissing, ref _objectMissing,
            ref _objectMissing, ref _objectMissing);

        object iconObjectFileName = _objectMissing;
        object objectClassType = FileType;
        object objectFilename = _fileInfo.ToString();

        Microsoft.Office.Interop.Word.InlineShape inlineShape = null;

        if (_displayAsIcon)
        {
            if (ObjectIcon != null)
            {
                using (FileStream iconStream = new FileStream(ObjectIconFile, FileMode.Create))
                {
                    ObjectIcon.Save(iconStream);
                    iconObjectFileName = ObjectIconFile;
                }
            }

            object objectIconLabel = _fileInfo.Name;

            inlineShape = wordDocument.InlineShapes.AddOLEObject(ref objectClassType,
                ref objectFilename, ref _objectFalse, ref _objectTrue, ref iconObjectFileName,
                ref _objectMissing, ref objectIconLabel, ref _objectMissing);
        }
        else
        {
            try
            {
                Image image = Image.FromFile(_fileInfo.ToString());
                _objectIsPicture = true;
                OleImageStyle = String.Format("height:{0}pt;width:{1}pt", image.Height, image.Width);

                wordDocument.InlineShapes.AddPicture(_fileInfo.ToString(), ref _objectMissing, ref _objectTrue, ref _objectMissing);
            }
            catch
            {
                inlineShape = wordDocument.InlineShapes.AddOLEObject(ref objectClassType,
                    ref objectFilename, ref _objectFalse, ref _objectFalse, ref _objectMissing, ref _objectMissing,
                    ref _objectMissing, ref _objectMissing);
            }
        }

        WordOpenXml = wordDocument.Range(ref _objectMissing, ref _objectMissing).WordOpenXML;

        if (_objectIsPicture)
        {
            OleObjectBinaryData = GetPictureBinaryData();
            OleImageBinaryData = GetPictureBinaryData();
        }
        else
        {
            OleObjectBinaryData = GetOleBinaryData(_oleObjectDataTag);
            OleImageBinaryData = GetOleBinaryData(_oleImageDataTag);
        }

        // Not sure why, but Excel seems to hang in the processes if you attach an Excel file…
        // This kills the excel process that has been started < 15 seconds ago (so not to kill the user's other Excel processes that may be open)
        if (FileType.StartsWith("Excel"))
        {
            Process[] processes = Process.GetProcessesByName("EXCEL");
            foreach (Process process in processes)
            {
                if (DateTime.Now.Subtract(process.StartTime).Seconds <= 15)
                {
                    process.Kill();
                    break;
                }
            }
        }

        wordDocument.Close(ref _objectFalse, ref _objectMissing, ref _objectMissing);
        wordApplication.Quit(ref _objectMissing, ref _objectMissing, ref _objectMissing);
    }

    /// <summary>
    /// Gets the binary data from the Xml File that is associated with the Tag passed in
    /// </summary>
    /// <param name="binaryDataXmlTag">the Tag to look for in the OpenXml</param>
    /// <returns></returns>
    private string GetOleBinaryData(string binaryDataXmlTag)
    {
        string binaryData = null;
        if (BinaryDataXmlNodesList != null)
        {
            foreach (XmlNode xmlNode in BinaryDataXmlNodesList)
            {
                if (xmlNode.ParentNode != null)
                {
                    foreach (XmlAttribute attr in xmlNode.ParentNode.Attributes)
                    {
                        if (String.IsNullOrEmpty(binaryData) && attr.Value.Contains(binaryDataXmlTag))
                        {
                            binaryData = xmlNode.InnerText;
                            break;
                        }
                    }
                }
            }
        }

        return binaryData;
    }

    /// <summary>
    /// Gets the image Binary data, if the file is an image
    /// </summary>
    /// <returns></returns>
    private string GetPictureBinaryData()
    {
        string binaryData = null;
        if (BinaryDataXmlNodesList != null)
        {
            foreach (XmlNode xmlNode in BinaryDataXmlNodesList)
            {
                binaryData = xmlNode.InnerText;
                break;
            }
        }

        return binaryData;
    }

    /// <summary>
    /// Gets the file type description ("Application", "Text Document", etc.) for the file.
    /// </summary>
    /// <param name="fileInfo">FileInfo containing extention</param>
    /// <returns>Type Description</returns>
    public static string GetFileType(FileInfo fileInfo, bool returnDescription)
    {
        if (fileInfo == null)
        {
            throw new ArgumentNullException("fileInfo");
        }

        string description = "File";
        if (string.IsNullOrEmpty(fileInfo.Extension))
        {
            return description;
        }
        description = string.Format("{0} File", fileInfo.Extension.Substring(1).ToUpper());
        RegistryKey typeKey = Registry.ClassesRoot.OpenSubKey(fileInfo.Extension);
        if (typeKey == null)
        {
            return description;
        }
        string type = Convert.ToString(typeKey.GetValue(string.Empty));
        RegistryKey key = Registry.ClassesRoot.OpenSubKey(type);
        if (key == null)
        {
            return description;
        }

        if (returnDescription)
        {
            description = Convert.ToString(key.GetValue(string.Empty));
            return description;
        }
        else
        {
            return type;
        }
    }

    #endregion Methods
}
 _objectIcon = Enterprise.Windows.Win32.Win32.GetLargeIcon(_filePathAndName); 

好像壞了,但是

_objectIcon = System.Drawing.Icon.ExtractAssociatedIcon(_filePathAndName);

也應該工作。

在這里的回答將告訴您如何執行此操作,但不會展示SDK 或特定語言。

這是一個很好的答案,對我幫助很大,但用戶 bic 提到的潛在錯誤也存在

OpenXmlEmbeddedObject(FileInfo fileInfo, bool displayAsIcon)

在第 242 行,

_filePathAndName = fileInfo.ToString();

SetupOleFileInformation()

在第 264 行,

 object objectFilename = _fileInfo.ToString();

第 289 行,以及

Image image = Image.FromFile(_fileInfo.ToString());

第 293 行

wordDocument.InlineShapes.AddPicture(_fileInfo.toString(), ref _objectMissing, ref _objectTrue, ref _objectMissing);

如果代碼也應該使用相對路徑,所有這些都需要是“FullName”而不是“ToString()”。 希望這可以幫助任何想要使用 D Lyonnais 代碼的人!

復制您希望如何通過代碼獲取文檔,然后使用 Open XML SDK Tool 2.5 for Microsoft Office 編寫代碼。 這個工具反映了代碼。 您可以簡單地復制粘貼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM