c# - test - Al utilizar ASP.NET Web API, ¿cómo puede un controlador devolver una colección de imágenes transmitidas en tiempo real mediante la biblioteca DotNetZip?
task<iactionresult> (4)
Un enfoque más genérico funcionaría así:
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};
}
El método ZipContentResult
también podría vivir en una clase base y usarse desde cualquier otra acción en cualquier controlador de API.
¿Cómo puedo crear un controlador de API web que genera y devuelve un archivo comprimido comprimido transmitido desde una colección de archivos JPEG en memoria (objetos MemoryStream). Estoy intentando usar la Biblioteca DotNetZip. Encontré este ejemplo: http://www.4guysfromrolla.com/articles/092910-1.aspx#postadlink . Pero Response.OutputStream no está disponible en la API web, por lo que esa técnica no funciona. Por lo tanto, traté de guardar el archivo zip en un nuevo MemoryStream; pero arrojó. Por último, traté de usar PushStreamContent. Aquí está mi código:
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) arroja una referencia nula. Pero ni zipFile ni memStream son nulos y no hay una excepción interna. Entonces no estoy seguro de qué está causando la referencia nula. Tengo muy poca experiencia con Web API, secuencias de memoria y nunca antes había usado DotNetZipLibrary. Esta es una continuación de esta pregunta: ¿Quieres un controlador eficiente de Web API ASP.NET que pueda devolver de manera confiable archivos JPEG de 30 a 50 ~ 3MB?
¿Algunas ideas? ¡Gracias!
Solo tuve el mismo problema que tú.
zipFile.Save(outputStream); //I got Null Reference Exception here.
El problema era que estaba agregando archivos de una secuencia de memoria de esta manera:
zip.AddEntry(fileName, ms);
Todo lo que tienes que hacer es cambiarlo a esto:
zip.AddEntry(fileName, ms.ToArray());
Parece que cuando el escritor decide escribir el archivo e intenta leer la secuencia, la secuencia es basura o algo así ...
¡Aclamaciones!
La clase PushStreamContent se puede utilizar en este caso para eliminar la necesidad del MemoryStream, al menos para todo el archivo comprimido. Se puede implementar de esta manera:
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;
}
Idealmente, sería posible crear esto de forma más dinámica utilizando la clase ZipOutputStream para crear dinámicamente el archivo zip en lugar de usar ZipFile. En ese caso, el MemoryStream para cada mapa de bits no sería necesario.
public HttpResponseMessage GetItemsInZip(int id)
{
var itemsToWrite = // return array of objects based on id;
// create zip file stream
MemoryStream archiveStream = new MemoryStream();
using (ZipArchive archiveFile = new ZipArchive(archiveStream, ZipArchiveMode.Create, true))
{
foreach (var item in itemsToWrite)
{
// create file streams
// add the stream to zip file
var entry = archiveFile.CreateEntry(item.FileName);
using (StreamWriter sw = new StreamWriter(entry.Open()))
{
sw.Write(item.Content);
}
}
}
// return the zip file stream to http response content
HttpResponseMessage responseMsg = new HttpResponseMessage(HttpStatusCode.OK);
responseMsg.Content = new ByteArrayContent(archiveStream.ToArray());
archiveStream.Dispose();
responseMsg.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = "test.zip" };
responseMsg.Content.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
return responseMsg;
}
Framework usado .NET 4.6.1 con MVC 5