使用ASP.NET Web API,控制器如何返回使用DotNetZip Library压缩的流图像集合?

Cal*_*ale 14 .net c# dotnetzip asp.net-web-api

如何创建Web API控制器,生成并返回从内存中JPEG文件(MemoryStream对象)集合流式传输的压缩zip文件.我正在尝试使用DotNetZip库.我找到了这个例子:http://www.4guysfromrolla.com/articles/092910-1.aspx#postadlink.但是,Response.OutputStream在Web API中不可用,因此该技术不能正常工作.因此我尝试将zip文件保存到新的MemoryStream中; 但它扔了.最后,我尝试使用PushStreamContent.这是我的代码:

    public HttpResponseMessage Get(string imageIDsList) {
        var imageIDs = imageIDsList.Split(',').Select(_ => int.Parse(_));
        var any = _dataContext.DeepZoomImages.Select(_ => _.ImageID).Where(_ => imageIDs.Contains(_)).Any();
        if (!any) {
            throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
        }
        var dzImages = _dataContext.DeepZoomImages.Where(_ => imageIDs.Contains(_.ImageID));
        using (var zipFile = new ZipFile()) {
            foreach (var dzImage in dzImages) {
                var bitmap = GetFullSizeBitmap(dzImage);
                var memoryStream = new MemoryStream();
                bitmap.Save(memoryStream, ImageFormat.Jpeg);
                var fileName = string.Format("{0}.jpg", dzImage.ImageName);
                zipFile.AddEntry(fileName, memoryStream);
            }
            var response = new HttpResponseMessage(HttpStatusCode.OK);
            var memStream = new MemoryStream();
            zipFile.Save(memStream); //Null Reference Exception
            response.Content = new ByteArrayContent(memStream.ToArray());
            response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
            response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = string.Format("{0}_images.zip", dzImages.Count()) };
            return response;
        }
    }
Run Code Online (Sandbox Code Playgroud)

zipFile.Save(memStream)抛出空引用.但是zipFile和memStream都不为null,并且没有内部异常.所以我不确定是什么导致了null引用.我对Web API,内存流的经验很少,而且之前我从未使用过DotNetZipLibrary.这是对这个问题的跟进:想要一个高效的ASP.NET Web API控制器,它可以可靠地返回30到50~3MB的JPEG

有任何想法吗?谢谢!

Fel*_*ala 16

更通用的方法可以这样工作:

using Ionic.Zip; // from NUGET-Package "DotNetZip"

public HttpResponseMessage Zipped()
{
    using (var zipFile = new ZipFile())
    {
        // add all files you need from disk, database or memory
        // zipFile.AddEntry(...);

        return ZipContentResult(zipFile);
    }
}

protected HttpResponseMessage ZipContentResult(ZipFile zipFile)
{
    // inspired from http://stackoverflow.com/a/16171977/92756
    var pushStreamContent = new PushStreamContent((stream, content, context) =>
    {
        zipFile.Save(stream);
        stream.Close(); // After save we close the stream to signal that we are done writing.
    }, "application/zip");

    return new HttpResponseMessage(HttpStatusCode.OK) {Content = pushStreamContent};
}
Run Code Online (Sandbox Code Playgroud)

ZipContentResult方法也可以存在于基类中,并可以在任何api控制器中的任何其他操作中使用.

  • 这很棒.不知道为什么这个被忽视了 (2认同)

Ste*_*sky 6

PushStreamContent类可以在这种情况下使用,以消除对MemoryStream的需要,至少在整个压缩文件.它可以像这样实现:

    public HttpResponseMessage Get(string imageIDsList)
    {
        var imageIDs = imageIDsList.Split(',').Select(_ => int.Parse(_));
        var any = _dataContext.DeepZoomImages.Select(_ => _.ImageID).Where(_ => imageIDs.Contains(_)).Any();
        if (!any)
        {
            throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
        }
        var dzImages = _dataContext.DeepZoomImages.Where(_ => imageIDs.Contains(_.ImageID));
        var streamContent = new PushStreamContent((outputStream, httpContext, transportContent) =>
            {
                try
                {
                    using (var zipFile = new ZipFile())
                    {
                        foreach (var dzImage in dzImages)
                        {
                            var bitmap = GetFullSizeBitmap(dzImage);
                            var memoryStream = new MemoryStream();
                            bitmap.Save(memoryStream, ImageFormat.Jpeg);
                            memoryStream.Position = 0;
                            var fileName = string.Format("{0}.jpg", dzImage.ImageName);
                            zipFile.AddEntry(fileName, memoryStream);
                        }
                        zipFile.Save(outputStream); //Null Reference Exception
                    }
                }
                finally
                {
                    outputStream.Close();
                }
            });
        streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
        streamContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
        {
            FileName = string.Format("{0}_images.zip", dzImages.Count()),
        };

        var response = new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = streamContent
            };

        return response;
    }
Run Code Online (Sandbox Code Playgroud)

理想情况下,使用ZipOutputStream类动态创建zip而不是使用ZipFile 可以更加动态地创建它.在这种情况下,不需要每个位图的MemoryStream.