So i am trying to create a zip archive and return it from my web api. The controller is called from an angular 2 site. Currently the zip file is created but when i open it i get an invalid message. Originally i had the streams in using statements but had to change that as they were being disposed before the request had completed.
What i need is to create the zip file, add the csv file to its content. And then return the zip file. But the zip file is always invalid. I have read the zip archive needs to be disposed in order for it write its contents, however im not sure what is the best way to implement this. Thank you for any guidance.
public async Task<IHttpActionResult> ExportReport(int id, ReportModel report)
{
try
{
var result = await ReportGenerationService.ExportReportForId(id, report.Page, report.PageSize, report.SortField, report.SortDir, report.SearchTerm, report.StartDate, report.EndDate, report.UserId, report.TeamId, report.SelectedDateItem);
if (result != null)
{
var compressedFileStream = new MemoryStream();
var zipArchive = new ZipArchive(compressedFileStream, ZipArchiveMode.Update, false);
var zipEntry = zipArchive.CreateEntry("textExport.csv");
var origionalFileSteam = new MemoryStream(result.ExportToBytes());
var zipEntryStream = zipEntry.Open();
origionalFileSteam.CopyTo(zipEntryStream);
var response = new HttpResponseMessage(HttpStatusCode.OK) {Content = new StreamContent(compressedFileStream)};
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "Export.zip"
};
var t = compressedFileStream.CanRead;
return ResponseMessage(response);
}
return NotFound();
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
In Response to using statements:
At one point i had everything wrapped in using statements but the response would fail because the stream had already been disposed. You can see this below.
public async Task<IHttpActionResult> ExportReport(int id, ReportModel report)
{
try
{
var result = await ReportGenerationService.ExportReportForId(id, report.Page, report.PageSize, report.SortField, report.SortDir, report.SearchTerm, report.StartDate, report.EndDate, report.UserId, report.TeamId, report.SelectedDateItem);
if (result != null)
{
var compressedFileStream = new MemoryStream();
var zipArchive = new ZipArchive(compressedFileStream, ZipArchiveMode.Update, false);
//Create a zip entry for each attachment
var zipEntry = zipArchive.CreateEntry("textExport.csv");
//Get the stream of the attachment
using (var originalFileStream = new MemoryStream(result.ExportToBytes()))
using (var zipEntryStream = zipEntry.Open()) {
//Copy the attachment stream to the zip entry stream
originalFileStream.CopyTo(zipEntryStream);
}
compressedFileStream.Position = 0;
var response = new HttpResponseMessage(HttpStatusCode.OK) {Content = new StreamContent(compressedFileStream)};
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "Export.zip"
};
return ResponseMessage(response);
}
return NotFound();
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
You are missing a handful of
using(){}
blocks.Make sure you close the originalFileSteam, zipEntryStream and the zipArchive in the proper order.
And just to be sure, reset the memoryStream. I don't know if this is needed but it won't hurt.
The memory stream is being disposed when the zip archive is disposed.
You should dispose of the archive to force it to write its content to its underlying memory stream, but take note of the following
And since you want to continue using the memory stream then you need to make sure it remains open and that the stream pointer is reset so that is can be read from the beginning.