Devolución de archivo binario del controlador en ASP.NET Web API

Estoy trabajando en un servicio web utilizando la nueva WebAPI de ASP.NET MVC que servirá los archivos binarios, principalmente archivos .cab y .exe .

El siguiente método de control parece funcionar, lo que significa que devuelve un archivo, pero está configurando el tipo de contenido para application/json :

 public HttpResponseMessage Post(string version, string environment, string filetype) { var path = @"C:\Temp\test.exe"; var stream = new FileStream(path, FileMode.Open); return new HttpResponseMessage(stream, new MediaTypeHeaderValue("application/octet-stream")); } 

¿Hay una mejor manera de hacer esto?

Intente usar un HttpResponseMessage simple con su propiedad Content configurada a StreamContent :

 // using System.IO; // using System.Net.Http; // using System.Net.Http.Headers; public HttpResponseMessage Post(string version, string environment, string filetype) { var path = @"C:\Temp\test.exe"; HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK); var stream = new FileStream(path, FileMode.Open, FileAccess.Read); result.Content = new StreamContent(stream); result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); return result; } 

Algunas cosas que debe tener en cuenta sobre la stream utilizada:

  • No debe invocar stream.Dispose() , ya que la API web aún necesita poder acceder a ella cuando procese el result del método de la controladora para enviar datos al cliente. Por lo tanto, no use un bloque using (var stream = …) . La API web eliminará la transmisión por usted.

  • Asegúrese de que la secuencia tenga su posición actual configurada en 0 (es decir, el comienzo de los datos de la secuencia). En el ejemplo anterior, esto es un hecho dado que acaba de abrir el archivo. Sin embargo, en otros escenarios (como cuando primero escribes algunos datos binarios en un MemoryStream ), asegúrate de hacer stream.Seek(0, SeekOrigin.Begin); o establecer stream.Position = 0;

  • Con las secuencias de archivos, especificar explícitamente el permiso FileAccess.Read puede ayudar a prevenir problemas de derechos de acceso en los servidores web; A las cuentas del grupo de aplicaciones de IIS a menudo solo se les otorgan derechos de acceso de lectura / lista / ejecución al wwwroot.

Para Web API 2 , puede implementar IHttpActionResult . Aquí está el mío:

 class FileResult : IHttpActionResult { private readonly string _filePath; private readonly string _contentType; public FileResult(string filePath, string contentType = null) { if (filePath == null) throw new ArgumentNullException("filePath"); _filePath = filePath; _contentType = contentType; } public Task ExecuteAsync(CancellationToken cancellationToken) { var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StreamContent(File.OpenRead(_filePath)) }; var contentType = _contentType ?? MimeMapping.GetMimeMapping(Path.GetExtension(_filePath)); response.Content.Headers.ContentType = new MediaTypeHeaderValue(contentType); return Task.FromResult(response); } } 

Entonces algo como esto en tu controlador:

 [Route("Images/{*imagePath}")] public IHttpActionResult GetImage(string imagePath) { var serverPath = Path.Combine(_rootPath, imagePath); var fileInfo = new FileInfo(serverPath); return !fileInfo.Exists ? (IHttpActionResult) NotFound() : new FileResult(fileInfo.FullName); } 

Y aquí hay una manera en que puede decirle a IIS que ignore las solicitudes con una extensión para que la solicitud llegue al controlador:

    

Si bien la solución sugerida funciona bien, existe otra forma de devolver una matriz de bytes desde el controlador, con el flujo de respuesta correctamente formateado:

  • En la solicitud, configure el encabezado “Aceptar: application / octet-stream”.
  • En el lado del servidor, agregue un formateador de tipo de medio para admitir este tipo de mimo.

Desafortunadamente, WebApi no incluye ningún formateador para “application / octet-stream”. Aquí hay una implementación en GitHub: BinaryMediaTypeFormatter (hay adaptaciones menores para hacer que funcione para webapi 2, las firmas de método han cambiado).

Puede agregar este formateador a su configuración global:

 HttpConfiguration config; // ... config.Formatters.Add(new BinaryMediaTypeFormatter(false)); 

WebApi ahora debería usar BinaryMediaTypeFormatter si la solicitud especifica el encabezado Accept correcto.

Prefiero esta solución porque un controlador de acción que devuelve byte [] es más cómodo de probar. Sin embargo, la otra solución le permite un mayor control si desea devolver otro tipo de contenido que “application / octet-stream” (por ejemplo, “image / gif”).

La sobrecarga que está utilizando establece la enumeración de formateadores de serialización. Debe especificar el tipo de contenido explícitamente como:

 httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); 

Para cualquiera que tenga el problema de que se llame a la API más de una vez mientras se descarga un archivo bastante grande utilizando el método en la respuesta aceptada, configure el búfer de respuesta en verdadero System.Web.HttpContext.Current.Response.Buffer = true;

Esto asegura que todo el contenido binario se almacena en el servidor antes de enviarlo al cliente. De lo contrario, verá que se envía una solicitud múltiple al controlador y si no lo maneja correctamente, el archivo se dañará.

Tu podrías intentar

 httpResponseMessage.Content.Headers.Add("Content-Type", "application/octet-stream");