How can I create a Web API controller that generates and returns a compressed zip file streamed from a collection of in-memory JPEG files (MemoryStream objects). I'm attempting to use DotNetZip Library. I found this example: http://www.4guysfromrolla.com/articles/092910-1.aspx#postadlink. But the Response.OutputStream is not available in Web API and so that technique doesn't quite work. Therefore I tried saving the zip file to a new MemoryStream; but it threw. Lastly, I tried using PushStreamContent. Here's my code:
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;
}
}
zipFile.Save(memStream) throws null reference. But neither zipFile nor memStream are null and there is no internal exception. So I'm not sure what's causing the null reference. I've very little experience with Web API, memory streams, and I've never used DotNetZipLibrary before. This is a follow up to this question: Want an efficient ASP.NET Web API controller that can reliably return 30 to 50 ~3MB JPEGs
Any ideas? thanks!
The PushStreamContent class can be used in this case to eliminate the need for the MemoryStream, at least for the whole zip file. It can be implemented like this:
Ideally it would be possible to make this even more dynamically created using the ZipOutputStream class to dynamically create the zip instead of using ZipFile. In that case the MemoryStream for each bitmap would not be needed.
Used Framework .NET 4.6.1 with MVC 5
I just had the same problem as you.
The problem was that I was adding files from a memory stream like so:
All you have to do is change it to this:
It seems that when the writer decides to actually write the file and tries to read the stream, the stream is garbage collected or sth...
Cheers!
A more generic approach would work like this:
The
ZipContentResult
method could also live in a base class and be used from any other action in any api controller.