[英]error:“File is used by another process” while generating document in openxml
[英]Error when previewing document in zip file created by OpenXML
我使用 OpenXML 工具使用 ZipArchive 創建單詞和 excel 文件和 zip 的字節數組並返回文件字節。
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 文件后即可下載並打開該文件。
但是,window explorer 或其他解壓軟件無法查看。 嘗試在 window 資源管理器中打開文檔時,出現錯誤消息
“Windows 無法完成提取。無法創建目標文件”
關於如何解決這個問題的任何想法? 可以在 OpenXML 創建的 zip 中查看文檔嗎?
更新:我正在使用“打開 XML SDK 2.5 生產力工具”來生成代碼。 下面的代碼是生成文檔的代碼。 (詳情請使用工具生成代碼,因為代碼太多了)
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 function 傳遞數組字節列表。
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);
}
}
}
我修改了單元測試以證明這適用於多個文檔。 這是第一個:
[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 存儲庫中找到。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.