You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何通过C#的OpenXML从Docx文件中提取OLE包?

解决OpenXML提取docx中OLE包(ZIP文件)的问题

我来帮你搞定这个问题!你现在遇到的问题有两个核心点,咱们一步步拆解解决:

1. 你的原代码的明显错误

先看你写的这段代码片段:

System.IO.Stream dt = item.GetStream(FileMode.OpenOrCreate);
BinaryWriter writer = new BinaryWriter(dt);
byte[] bt = new byte[dt.Length];
using (FileStream fs = File.Open($"C:\\Users\\宇宙无敌帅小伙\\Desktop\\{cnt}.zip", FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite)) {
 fs.Write(bt, 0, bt.Length);
}

你创建了bt数组,但完全没有从dt流里读取任何数据到数组中,所以写入的文件全是空字节,自然无法打开。哪怕你修复了这个问题,直接保存的还是OLE容器流,不是原始ZIP文件,依然会损坏。

2. 核心问题:OLE包的结构原理

当你在Word中插入ZIP作为OLE包时,Word会把原始文件封装在OLE复合文档容器里,EmbeddedObjectPart存储的就是这个容器数据,而非原始ZIP文件。要拿到真实的ZIP内容,必须解析这个OLE容器,提取里面嵌套的实际文件流。

解决方案:使用OpenMcdf解析OLE复合文档

最可靠的方法是用专门处理OLE复合文档的OpenMcdf库,它能轻松解析容器结构,精准提取我们需要的内容。

步骤1:安装OpenMcdf NuGet包

在你的项目中安装NuGet包OpenMcdf(这是处理OLE复合文档的成熟、稳定库)。

步骤2:修正后的提取代码

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

class Program {
 static void Main(string[] args) {
 string filepath = @"C:\Users\宇宙无敌帅小伙\Desktop\test.docx";
 ExtractOlePackageFiles(filepath);
 }

 public static void ExtractOlePackageFiles(string docxPath) {
 try {
 using (WordprocessingDocument docx = WordprocessingDocument.Open(docxPath, false)) {
 int cnt = 0;
 foreach (EmbeddedObjectPart olePart in docx.MainDocumentPart.EmbeddedObjectParts) {
 // 打开OLE复合文档流
 using (Stream oleStream = olePart.GetStream(FileMode.Open))
 using (CompoundFile cf = new CompoundFile(oleStream)) {
 // 找到OLE包中的实际内容流(通常名为"Package")
 CfStream packageStream = cf.RootStorage.GetStream("Package");
 if (packageStream != null) {
 // 读取流内容并保存为ZIP文件
 byte[] fileContent = packageStream.GetData();
 string outputPath = $"C:\\Users\\宇宙无敌帅小伙\\Desktop\\{cnt}.zip";
 File.WriteAllBytes(outputPath, fileContent);
 Console.WriteLine($"成功提取文件:{outputPath}");
 }
 cnt++;
 }
 }
 }
 } catch (Exception e) {
 Console.WriteLine($"提取失败:{e.Message}");
 Console.WriteLine(e.StackTrace);
 }
 }
}

代码说明

  • CompoundFile类打开EmbeddedObjectPart的流,解析OLE复合文档的内部结构。
  • OLE包的实际文件内容通常存储在名为Package的子流中,通过cf.RootStorage.GetStream("Package")精准定位这个流。
  • 最后把流中的字节写入文件,得到的就是你插入的原始ZIP文件了。

备选方案:手动解析OLE头(不依赖第三方库)

如果你不想引入第三方库,可以尝试手动跳过OLE头(注意:这种方法兼容性稍差,不同版本的OLE结构可能有差异):

public static void ExtractOlePackageWithoutLibrary(string docxPath) {
 try {
 using (WordprocessingDocument docx = WordprocessingDocument.Open(docxPath, false)) {
 int cnt = 0;
 foreach (EmbeddedObjectPart olePart in docx.MainDocumentPart.EmbeddedObjectParts) {
 using (Stream oleStream = olePart.GetStream(FileMode.Open))
 using (FileStream fs = new FileStream($"C:\\Users\\宇宙无敌帅小伙\\Desktop\\{cnt}.zip", FileMode.Create)) {
 // 跳过OLE复合文档头(通常前0x110字节是头信息,具体可能需要根据实际情况调整)
 oleStream.Seek(0x110, SeekOrigin.Begin);
 oleStream.CopyTo(fs);
 }
 Console.WriteLine($"提取文件:{cnt}.zip");
 cnt++;
 }
 }
 } catch (Exception e) {
 Console.WriteLine($"错误:{e.Message}");
 }
}

这种方法通过跳过固定长度的OLE头来提取内容,但如果OLE结构有变化,可能会失败,所以更推荐使用OpenMcdf的方案。

内容的提问来源于stack exchange,提问作者lishuaige

火山引擎 最新活动