简体   繁体   English

在内存中创建Zip存档,然后从Web API返回

[英]Creating Zip Archive in memory, and returning it from a web api

So i am trying to create a zip archive and return it from my web api. 因此,我正在尝试创建一个zip存档,并从我的网络api返回它。 The controller is called from an angular 2 site. 从角度2站点调用控制器。 Currently the zip file is created but when i open it i get an invalid message. 当前,该zip文件已创建,但是当我打开它时,收到无效消息。 Originally i had the streams in using statements but had to change that as they were being disposed before the request had completed. 最初,我在using语句中具有流,但是必须更改它们,因为它们在请求完成之前就被丢弃了。

What i need is to create the zip file, add the csv file to its content. 我需要的是创建zip文件,将csv文件添加到其内容。 And then return the zip file. 然后返回压缩文件。 But the zip file is always invalid. 但是zip文件始终无效。 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. 我已经阅读了需要处理的zip归档文件,以便写入其内容,但是我不确定实现此目标的最佳方法是什么。 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: 响应using语句:

At one point i had everything wrapped in using statements but the response would fail because the stream had already been disposed. 在某一时刻,我将所有内容都包裹在using语句中,但是响应将失败,因为流已经被处理掉了。 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. 您缺少一些using(){}块。

Make sure you close the originalFileSteam, zipEntryStream and the zipArchive in the proper order. 确保以正确的顺序关闭originalFileSteam,zipEntryStream和zipArchive。

And just to be sure, reset the memoryStream. 并且可以肯定的是,重置memoryStream。 I don't know if this is needed but it won't hurt. 我不知道是否需要这样做,但不会造成伤害。

//using (var compressedFileStream = new MemoryStream())
var compressedFileStream = new MemoryStream();

using (var zipArchive = new ZipArchive(...)) 
{
        //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 responseBytes =new MemoryStream(compressedFileStream.ToArray());

var response = new HttpResponseMessage(HttpStatusCode.OK) {Content = new StreamContent(responseBytes )};
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");

return ResponseMessage(response);

The memory stream is being disposed when the zip archive is disposed. 当处理zip存档时,将处理内存流。

You should dispose of the archive to force it to write its content to its underlying memory stream, but take note of the following 您应该处理存档,以强制其将内容写入其基础内存流,但请注意以下几点

ZipArchive.Dispose() ZipArchive.Dispose()

Unless you construct the object by using the ZipArchive(Stream, ZipArchiveMode, Boolean) constructor overload and set its leaveOpen parameter to true , all underlying streams are closed and no longer available for subsequent write operations. 除非您使用ZipArchive(Stream, ZipArchiveMode, Boolean)构造函数重载来构造对象,否则将其leaveOpen参数设置为trueleaveOpen所有基础流都将关闭,并且不再可用于后续的写操作。

When you are finished using this instance of ZipArchive , call Dispose() to release all resources used by this instance. 完成使用此ZipArchive实例后,调用Dispose()释放该实例使用的所有资源。 You should eliminate further references to this ZipArchive instance so that the garbage collector can reclaim the memory of the instance instead of keeping it alive for finalization. 您应该消除对该ZipArchive实例的进一步引用,以便垃圾收集器可以回收该实例的内存,而不是保留该内存以进行最终化。

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. 而且,由于要继续使用内存流,因此需要确保它保持打开状态,并且流指针已重置,以便可以从头开始读取。

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();
            using(var zipArchive = new ZipArchive(compressedFileStream, ZipArchiveMode.Create, 
                leaveOpen: true)) { //<--This is important to keep stream open
                //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
                    await originalFileStream.CopyToAsync(zipEntryStream);
                }
            }// disposal of archive will force data to be written/flushed to memory stream.
            compressedFileStream.Position = 0;//reset memory stream position.

            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);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM