我使用OpenXML工具创建了word和excel文件的字节数组,并使用ZipArchive对其进行压缩,然后返回filebyte。
httpResponseMessage = Request.CreateResponse(HttpStatusCode.OK);
httpResponseMessage.Content = new ByteArrayContent(zipFileBytes);
httpResponseMessage.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
httpResponseMessage.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/zip");
httpResponseMessage.Content.Headers.ContentLength = zipFileBytes.Length;
httpResponseMessage.Content.Headers.Add("xfilename", zipFileName);
httpResponseMessage.StatusCode = HttpStatusCode.OK;
httpResponseMessage.Content.Headers.Add("Access-Control-Expose-Headers", "xfilename");
return httpResponseMessage;
解压缩zip文件后,可以下载并打开该文件。
但是,它不能通过窗口资源管理器或其他解压缩软件进行审查。当尝试在窗口资源管理器中打开文档时,错误消息
"Windows无法完成提取。目标文件可能未创建">
关于如何解决这个问题有什么想法吗?可以在OpenXML创建的zip中查看文档吗?
更新:我正在使用"Open XML SDK 2.5 Productivity Tool"来生成代码。下面的代码就是生成文档的代码。(详细信息,请使用该工具生成代码,因为这是太多行代码(
using DocumentFormat.OpenXml.Packaging;
using Ap = DocumentFormat.OpenXml.ExtendedProperties;
using DocumentFormat.OpenXml.Wordprocessing;
using DocumentFormat.OpenXml;
using M = DocumentFormat.OpenXml.Math;
using Ovml = DocumentFormat.OpenXml.Vml.Office;
using V = DocumentFormat.OpenXml.Vml;
using W15 = DocumentFormat.OpenXml.Office2013.Word;
using A = DocumentFormat.OpenXml.Drawing;
using Thm15 = DocumentFormat.OpenXml.Office2013.Theme;
namespace GeneratedCode
{
public class GeneratedClass
{
// Creates a WordprocessingDocument.
public void CreatePackage(DataModel dataModel, List<DataModel> dataList, out string filename, out Byte[] fileBytes)
{
filename = string.Empty;
fileBytes = null;
using (MemoryStream ms = new MemoryStream())
{
try
{
using (WordprocessingDocument package = WordprocessingDocument.Create(ms, WordprocessingDocumentType.Document))
{
CreateParts(package, dataModel, dataList);
}
string extension = ".docx";
filename = "TestDoc" + extension;
fileBytes = ms.ToArray();
ms.Close();
return;
}
catch (System.Exception)
{
throw;
}
}
}
}
然后,我使用下面的代码生成zip文件,并从CreatePackage函数传递数组字节的列表。
public byte[] zipByteDocument(List<Tuple<Byte[], string>> fileBytes)
{
// the output bytes of the zip
byte[] zipFileBytes = null;
// create a working memory stream
using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream())
{
// create a zip
using (System.IO.Compression.ZipArchive zip = new System.IO.Compression.ZipArchive(memoryStream, System.IO.Compression.ZipArchiveMode.Create, true))
{
// interate through the source files
foreach (Tuple<Byte[], string> file in fileBytes)
{
// add the item name to the zip
System.IO.Compression.ZipArchiveEntry zipItem = zip.CreateEntry(file.Item2);
// add the item bytes to the zip entry by opening the original file and copying the bytes
using (System.IO.MemoryStream originalFileMemoryStream = new System.IO.MemoryStream(file.Item1))
{
using (System.IO.Stream entryStream = zipItem.Open())
{
originalFileMemoryStream.CopyTo(entryStream);
}
}
}
}
zipFileBytes = memoryStream.ToArray();
}
return zipFileBytes;
}
最后,我将zipFileBytes传递给httpResponseMessage,它就可以下载了。但是如果不解压缩zip文件,就无法预览。
我创建了一些单元测试(见下文(,这些测试表明您共享的代码不应该有问题(不过,请注意,我并没有简单地复制您的代码(。生成的代码或未共享的其他代码可能是罪魁祸首。
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using CodeSnippets.IO;
using Xunit;
namespace CodeSnippets.Tests.IO.Compression
{
public class ZipArchiveTests
{
private static byte[] CreateZipArchiveBytes(IEnumerable<(byte[], string)> files)
{
using MemoryStream stream = CreateZipArchiveStream(files);
return stream.ToArray();
}
private static MemoryStream CreateZipArchiveStream(IEnumerable<(byte[], string)> files)
{
var stream = new MemoryStream();
using (CreateZipArchive(stream, files))
return stream;
}
private static ZipArchive CreateZipArchive(Stream stream, IEnumerable<(byte[], string)> files)
{
if (stream == null) throw new ArgumentNullException(nameof(stream));
if (files == null) throw new ArgumentNullException(nameof(files));
var archive = new ZipArchive(stream, ZipArchiveMode.Create, true);
foreach ((byte[] fileContent, string fileName) in files)
{
ZipArchiveEntry archiveEntry = archive.CreateEntry(fileName);
using Stream entryStream = archiveEntry.Open();
entryStream.Write(fileContent, 0, fileContent.Length);
}
return archive;
}
private static ZipArchive ReadZipArchive(byte[] zipArchiveBytes)
{
return new ZipArchive(new MemoryStream(zipArchiveBytes), ZipArchiveMode.Read, false);
}
private static byte[] ReadEntryBytes(ZipArchive zipArchive, string entryName)
{
ZipArchiveEntry entry = zipArchive.GetEntry(entryName) ?? throw new Exception();
var entryBytes = new byte[entry.Length];
using Stream entryStream = entry.Open();
entryStream.Read(entryBytes, 0, (int) entry.Length);
return entryBytes;
}
private static HttpResponseMessage CreateResponseMessage(byte[] content, string fileName, string mediaType)
{
var message = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(content)
};
message.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = fileName
};
message.Content.Headers.ContentType = new MediaTypeHeaderValue(mediaType);
message.Content.Headers.ContentLength = content.Length;
return message;
}
[Fact]
public async Task CreateResponseMessage_ZipArchiveBytes_Success()
{
// Arrange.
const string path = "Resources\ZipContents.docx";
string fileName = Path.GetFileName(path);
byte[] fileContent = File.ReadAllBytes(path);
byte[] zipArchiveBytes = CreateZipArchiveBytes(new[]
{
(fileContent, fileName)
});
// Act.
using HttpResponseMessage message = CreateResponseMessage(zipArchiveBytes, "ZipArchive.zip", "application/zip");
HttpContent messageContent = message.Content;
byte[] messageBytes = await messageContent.ReadAsByteArrayAsync();
// Assert.
// Original zipArchiveBytes and recevied messageBytes are equal.
Assert.Equal(zipArchiveBytes, messageBytes);
// Original file content and received ZIP archive content are equal.
using ZipArchive zipArchive = ReadZipArchive(messageBytes);
byte[] entryContent = ReadEntryBytes(zipArchive, fileName);
Assert.Equal(fileContent.Length, entryContent.Length);
Assert.Equal(fileContent, entryContent);
}
[Fact]
public void CreateZipArchiveBytes_WordDocument_ZipFileSuccessfullyCreated()
{
// Arrange.
const string path = "Resources\ZipContents.docx";
string fileName = Path.GetFileName(path);
byte[] fileContent = File.ReadAllBytes(path);
// Act.
byte[] zipArchiveBytes = CreateZipArchiveBytes(new[]
{
(fileContent, fileName)
});
File.WriteAllBytes("ZipArchive_Bytes.zip", zipArchiveBytes);
// Assert.
using ZipArchive zipArchive = ReadZipArchive(zipArchiveBytes);
byte[] entryContent = ReadEntryBytes(zipArchive, fileName);
Assert.Equal(fileContent.Length, entryContent.Length);
Assert.Equal(fileContent, entryContent);
}
}
}
更新2019-12-06
我修改了单元测试,以证明这可以用于多个文档。这是第一个:
[Fact]
public void CreateZipArchiveBytes_Directory_ZipFileSuccessfullyCreated()
{
// Arrange, creating a ZIP archive with more than one entry.
List<(byte[] fileContent, string fileName)> files = Directory
.EnumerateFiles("Resources")
.Select(path => (File.ReadAllBytes(path), Path.GetFileName(path)))
.ToList();
Assert.True(files.Count > 1);
// Act.
byte[] zipArchiveBytes = CreateZipArchiveBytes(files);
File.WriteAllBytes("ZipArchive_Directory.zip", zipArchiveBytes);
// Assert.
using ZipArchive zipArchive = ReadZipArchive(zipArchiveBytes);
Assert.Equal(files.Count, zipArchive.Entries.Count);
foreach (ZipArchiveEntry entry in zipArchive.Entries)
{
byte[] fileContent = files
.Where(file => file.fileName == entry.Name)
.Select(file => file.fileContent)
.Single();
using Stream entryStream = entry.Open();
byte[] entryContent = entryStream.ToArray();
Assert.Equal(fileContent, entryContent);
}
}
下一个单元测试将与HttpResponseMessage
一起演示相同的内容。
[Fact]
public async Task CreateResponseMessage_ZipArchiveDirectory_Success()
{
// Arrange, creating a ZIP archive with more than one entry.
List<(byte[] fileContent, string fileName)> files = Directory
.EnumerateFiles("Resources")
.Select(path => (File.ReadAllBytes(path), Path.GetFileName(path)))
.ToList();
Assert.True(files.Count > 1);
byte[] zipArchiveBytes = CreateZipArchiveBytes(files);
// Act.
using HttpResponseMessage message = CreateResponseMessage(zipArchiveBytes, "ZipArchive.zip", "application/zip");
HttpContent messageContent = message.Content;
byte[] messageBytes = await messageContent.ReadAsByteArrayAsync();
// Assert.
// Original zipArchiveBytes and recevied messageBytes are equal.
Assert.Equal(zipArchiveBytes, messageBytes);
// Original directory content and received ZIP archive content are equal.
using ZipArchive zipArchive = ReadZipArchive(messageBytes);
Assert.Equal(files.Count, zipArchive.Entries.Count);
foreach (ZipArchiveEntry entry in zipArchive.Entries)
{
byte[] fileContent = files
.Where(file => file.fileName == entry.Name)
.Select(file => file.fileContent)
.Single();
await using Stream entryStream = entry.Open();
byte[] entryContent = await entryStream.ToArrayAsync();
Assert.Equal(fileContent, entryContent);
}
}
核心方法没有改变。
完整的源代码可以在我的CodeSnippets GitHub存储库中找到。