关于c#:打开使用EPPlus创建的.xlsx文件并使用ICSharpCode.SharpZipLib压缩到一个文件夹时出现问题

Problems opening .xlsx file created with EPPlus and zipped in a folder with ICSharpCode.SharpZipLib

我正在使用epplus创建ExcelPackages(XLSX文档)列表,并将其作为zipEntries添加到zipOutputstream中。我认为Excel文档应该是有效的,因为我可以在不压缩的情况下将其中一个文档写入响应对象时打开它们。Zip文件夹是按预期创建的,文件在其中,似乎不为空,但当我尝试打开它们时,在Excel中会出现以下错误:

0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
List<ExcelPackage> documents = new List<ExcelPackage>();
List<string> fileNames = new List<string>();

//Code for fetching documents and filenames here (removed for the sake of readability)

Response.Clear();
Context.Response.BufferOutput = false;
Response.ContentType ="application/zip";
Response.AppendHeader("content-disposition","attachment; filename="random-foldername.zip"");
Response.CacheControl ="Private";
Response.Cache.SetExpires(DateTime.Now.AddMinutes(3));

ZipOutputStream zipOutputStream = new ZipOutputStream(Response.OutputStream);
zipOutputStream.SetLevel(3); //0-9, 9 being the highest level of compression
byte[] buffer = null;

for (int i = 0; i < documents.Count; i++)
{
    MemoryStream ms = new MemoryStream();

    documents[i].SaveAs(ms);

    ZipEntry entry = new ZipEntry(ZipEntry.CleanName(fileNames[i]));

    zipOutputStream.PutNextEntry(entry);
    buffer = new byte[ms.Length];

    ms.Read(buffer, 0, buffer.Length);
    entry.Size = ms.Length;

    ms.Close();

    zipOutputStream.Write(buffer, 0, buffer.Length);
    zipOutputStream.CloseEntry();

}
zipOutputStream.Finish();
zipOutputStream.Close();

Response.End();

至于文件名列表,我只是根据一些任意的东西生成一个名称,并在其末尾添加一个扩展名".xlsx"。

我不知道我在哪里出错了,有什么建议吗?


您必须倒带内存流才能读取某些内容(在写入操作之后,文件指针即为其结尾):

1
2
ms.Seek(0, SeekOrigin.Begin)
ms.Read(buffer, 0, buffer.Length);

也就是说,MemoryStream只不过是一个字节数组,因此您甚至不需要分配和读取一个新的数组,然后使用以下代码:

1
2
3
4
5
6
7
8
buffer = new byte[ms.Length];

ms.Read(buffer, 0, buffer.Length);
entry.Size = ms.Length;

ms.Close();

zipOutputStream.Write(buffer, 0, buffer.Length);

可以简单地替换为:

2

最后一点:如果您不想使用内部MemoryStream缓冲区(出于任何原因),并且您想要它的修剪副本(手工操作),那么只需使用这样的ToArray()方法:

1
2
3
4
var buffer = ms.ToArray();
ms.Close();
entry.Size = buffer.Length;
zipOutputStream.Write(buffer, 0, buffer.Length);