繁体   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