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

我花了很多时间试图找出一种使用OpenXml 2.0将任何文件嵌入Microsoft Word的好方法; Office文档相当容易,但其他文件类型如PDF,TXT,GIF,JPG,HTML等等。

在C#中使用任何文件类型的好方法是什么?

使用OpenXml 2.0将外来对象(PDF,TXT,GIF等)嵌入到Microsoft Word中(好吧,与COM合作)

我从这个网站得到了很多,所以我在这里问了一下我自己的问题,以便回答一些关于我难以找到答案的主题,希望它对人们有所帮助。

有几个例子展示了如何使用OpenXml 2.0将Office文档嵌入到另一个Office文档中,有什么不可以理解的是如何将任何文件嵌入到Office文档中。

我从其他人的代码中学到了很多东西,所以这是我的贡献。 由于我已经使用OpenXml生成文档,并且我需要将其他文件嵌入到Word中,因此我决定使用OpenXml和COM(Microsoft Office 2007 dll)的协作来实现我的目标。 如果你像我一样,“调用OLE服务器应用程序来创建一个IStorage”对你来说意义不大。

在这个例子中,我想展示我如何使用COM以编程方式获取附加文件的OLE二进制数据信息,然后我如何在OpenXml文档中使用该信息。 基本上,我是以编程方式查看OpenXml 2.0文档reflection器以获取我需要的信息。

我的下面的代码分为几个类,但这里是我正在做的概述:

  1. 创建一个OpenXml WordProcessingDocument,获取要嵌入的文件的System.IO.FileInfo
  2. 创建一个自定义的OpenXmlEmbeddedObject对象(这是包含所有二进制数据的对象)
  3. 使用上述步骤中的二进制数据创建数据流和图像流
  4. 将这些Streams用作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 { ///  /// Appends an Embedded Object into the specified Main Document ///  /// The MainDocument Part of your OpenXml Word Doc /// The FileInfo object associated with the file being embedded /// Whether or not to display the embedded file as an Icon (Otherwise it will display a snapshot of the file) 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( openXmlEmbeddedObject.FileContentType, embeddedPackagePartId); embeddedObjectPart.FeedData(dataStream); } else { EmbeddedObjectPart embeddedObjectPart = mainDocumentPart.AddNewPart( 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); } } } } } ///  /// Gets Paragraph that includes the embedded object ///  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; } ///  /// Gets a Unique ID for an XML Item, for reference purposes ///  /// A GUID string with removed dashes 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属性; 它们包含文件数据的64Bit字符串二进制信息和’.emf’图像。 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 ///  /// The File Type, as stored in Registry (Ex: a GIF Image = 'giffile') ///  public string FileType { get { if (String.IsNullOrEmpty(_fileType) && _fileInfo != null) { _fileType = GetFileType(_fileInfo, false); } return _fileType; } } private string _fileType; ///  /// 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 ///  public string FileContentType { get { if (String.IsNullOrEmpty(_fileContentType) && _fileInfo != null) { _fileContentType = GetFileContentType(_fileInfo); if (!_fileContentType.Contains("officedocument")) { _fileContentType = _defaultOleContentType; } } return _fileContentType; } } private string _fileContentType; ///  /// Gets the ContentType Text for the file ///  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; ///  /// The OpenXml information for the Word Application that is created (Make-Shoft Code Reflector) ///  public string WordOpenXml { get { return _wordOpenXml; } set { _wordOpenXml = value; } } private String _wordOpenXml; ///  /// The XmlDocument that is created based on the OpenXml Data from WordOpenXml ///  public XmlDocument OpenXmlDocument { get { if (_openXmlDocument == null && !String.IsNullOrEmpty(WordOpenXml)) { _openXmlDocument = new XmlDocument(); _openXmlDocument.LoadXml(WordOpenXml); } return _openXmlDocument; } } private XmlDocument _openXmlDocument; ///  /// The XmlNodeList, for all Nodes containing 'binaryData' ///  public XmlNodeList BinaryDataXmlNodesList { get { if (_binaryDataXmlNodesList == null && OpenXmlDocument != null) { _binaryDataXmlNodesList = OpenXmlDocument.GetElementsByTagName("pkg:binaryData"); } return _binaryDataXmlNodesList; } } private XmlNodeList _binaryDataXmlNodesList; ///  /// Icon Object for the file ///  public Icon ObjectIcon { get { if (_objectIcon == null) { _objectIcon = Enterprise.Windows.Win32.Win32.GetLargeIcon(_filePathAndName); } return _objectIcon; } } private Icon _objectIcon; ///  /// File Name for the Icon being created ///  public string ObjectIconFile { get { if (String.IsNullOrEmpty(_objectIconFile)) { _objectIconFile = String.Format("{0}.ico", _filePathAndName.Replace(".", "")); } return _objectIconFile; } } private string _objectIconFile; ///  /// Gets the original height and width of the emf file being created ///  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 ///  /// Generates binary information for the file being passed in ///  /// The FileInfo object for the file to be embedded /// Whether or not to display the file as an Icon (Otherwise it will show a snapshot view of the file) public OpenXmlEmbeddedObject(FileInfo fileInfo, bool displayAsIcon) { _fileInfo = fileInfo; _filePathAndName = fileInfo.ToString(); _displayAsIcon = displayAsIcon; SetupOleFileInformation(); } #endregion Constructor #region Methods ///  /// 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) ///  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); } ///  /// Gets the binary data from the Xml File that is associated with the Tag passed in ///  /// the Tag to look for in the OpenXml ///  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; } ///  /// Gets the image Binary data, if the file is an image ///  ///  private string GetPictureBinaryData() { string binaryData = null; if (BinaryDataXmlNodesList != null) { foreach (XmlNode xmlNode in BinaryDataXmlNodesList) { binaryData = xmlNode.InnerText; break; } } return binaryData; } ///  /// Gets the file type description ("Application", "Text Document", etc.) for the file. ///  /// FileInfo containing extention /// Type Description 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 } 

我的回答将告诉您如何执行此操作,但不会显示 SDK或特定语言。

  _objectIcon = Enterprise.Windows.Win32.Win32.GetLargeIcon(_filePathAndName); 

好像坏了,但是

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

也应该工作。

这是一个很好的答案,它帮助了我很多,但用户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代码的人!