问题
The question is how can I create a zipped (compressed) folder on the fly using ASP.NET Core 2 (currently) Web API?
I am using System.IO.Compression.ZipArchive
I have been several blog posts where this is done using streams or byte arrays, all give me the same output.
I am able to download the zip folder but unable to open it.
The zipped folder is the correct size. Even though it doesn't open.
They way I want it to work is for a user to click a button which runs this action and return a zipped folder containing one or multiple files.
[HttpGet]
[Route("/api/download/zip")]
public async Task<IActionResult> Zip()
{
byte[] bytes = null;
using (MemoryStream zipStream = new MemoryStream())
using (var zip = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
{
var tempFileName = await _azure.GetFilesByRef("Azure_FilePath");
// Running just this line gives me the zipped folder (empty) which I can open
ZipArchiveEntry entry = zip.CreateEntry("File1.pdf", CompressionLevel.Fastest);
// Adding this 2nd section will download the zip but will not open the zip folder
using (Stream stream = entry.Open())
using (FileStream fs = new FileStream(tempFileName, FileMode.Open, FileAccess.Read))
{
await fs.CopyToAsync(stream);
}
bytes = zipStream.ToArray();
}
return File(bytes, MediaTypeNames.Application.Zip, $"Attachments{DateTime.Now.ToBinary()}.zip");
}
Can anyone spot a mistake or suggest an alternative solution?
回答1:
All the data is not written to the stream until the archive is disposed. So in this case, the data in the stream may be incomplete if the archive has not flushed it as yet.
memoryStream.ToArray
is being called before the archive has a chance to flush all its data to the underlying stream.
Consider refactoring to
//...
var tempFileName = await _azure.GetFilesByRef("Azure_FilePath");
using (MemoryStream zipStream = new MemoryStream()) {
using (var archive = new ZipArchive(zipStream, ZipArchiveMode.Create, leaveOpen: true)) {
ZipArchiveEntry entry = archive.CreateEntry("File1.pdf", CompressionLevel.Fastest);
using (Stream stream = entry.Open())
using (FileStream fs = new FileStream(tempFileName, FileMode.Open, FileAccess.Read)) {
await fs.CopyToAsync(stream);
}
}// disposal of archive will force data to be written to memory stream.
zipStream.Position = 0; //reset memory stream position.
bytes = zipStream.ToArray(); //get all flushed data
}
//...
The assumption in your example is also that the FileStream
opened is the correct file type of the created entry; A single PDF file. Otherwise consider extracting a name from the tempFileName
.
You have to add a unique entry (file path) for each item you want added to the archive.
来源:https://stackoverflow.com/questions/50513852/using-ziparchive-with-asp-net-core-web-api