example - Devolviendo un archivo binario desde el controlador en ASP.NET Web API
web api pdf response (6)
Intente usar un HttpResponseMessage
simple con su propiedad de Content
establecida en un 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 a tener en cuenta sobre la stream
utilizada:
No debe llamar a
stream.Dispose()
, ya que la API web aún debe poder acceder a ella cuando procesa elresult
del método del controlador para enviar datos al cliente. Por lo tanto, no utilice un bloqueusing (var stream = …)
. La API web dispondrá de la transmisión por usted.Asegúrese de que el flujo tenga su posición actual establecida en 0 (es decir, el comienzo de los datos del flujo). En el ejemplo anterior, esto es un hecho dado que acaba de abrir el archivo. Sin embargo, en otros escenarios (como cuando escribe por primera vez algunos datos binarios en un
MemoryStream
), asegúrese destream.Seek(0, SeekOrigin.Begin);
o establecerstream.Position = 0;
Con las secuencias de archivos, la especificación explícita del permiso
FileAccess.Read
puede ayudar a prevenir problemas de derechos de acceso en los servidores web; Las cuentas del grupo de aplicaciones de IIS a menudo solo reciben derechos de acceso de lectura / lista / ejecución a wwwroot.
Estoy trabajando en un servicio web utilizando el nuevo WebAPI de ASP.NET MVC que servirá archivos binarios, principalmente archivos .cab
y .exe
.
El siguiente método de controlador parece funcionar, lo que significa que devuelve un archivo, pero está configurando el tipo de contenido en application/json
:
public HttpResponseMessage<Stream> Post(string version, string environment, string filetype)
{
var path = @"C:/Temp/test.exe";
var stream = new FileStream(path, FileMode.Open);
return new HttpResponseMessage<Stream>(stream, new MediaTypeHeaderValue("application/octet-stream"));
}
¿Hay una mejor manera de hacer esto?
La sobrecarga que está utilizando establece la enumeración de formateadores de serialización. Necesita especificar el tipo de contenido explícitamente como:
httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
Para cualquier persona que tenga el problema de que se llame más de una vez a la API mientras descarga un archivo bastante grande con el método de 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 búfer antes de enviarlo al cliente. De lo contrario, verá una solicitud múltiple que se envía al controlador y, si no la maneja correctamente, el archivo se dañará.
Para la API web 2 , puede implementar IHttpActionResult
. Aquí está el mío:
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
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<HttpResponseMessage> 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 forma en que puede decirle a IIS que ignore las solicitudes con una extensión para que la solicitud llegue al controlador:
<!-- web.config -->
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
Si bien la solución sugerida funciona bien, hay otra manera de devolver una matriz de bytes desde el controlador, con el flujo de respuesta correctamente formateado:
- En la solicitud, establezca el encabezado "Aceptar: aplicación / octet-stream".
- Del lado del servidor, agregue un formateador de tipo de medios para admitir este tipo mime.
Desafortunadamente, WebApi no incluye ningún formateador para "application / octet-stream". Hay una implementación aquí en GitHub: BinaryMediaTypeFormatter (hay adaptaciones menores para hacer que funcione para webapi 2, firmas de métodos cambiadas).
Puedes agregar este formateador en tu configuración global:
HttpConfiguration config;
// ...
config.Formatters.Add(new BinaryMediaTypeFormatter(false));
WebApi ahora debe usar BinaryMediaTypeFormatter
si la solicitud especifica el encabezado de aceptación correcto.
Prefiero esta solución porque un controlador de acción que devuelve el byte [] es más cómodo de probar. Sin embargo, la otra solución le permite tener más control si desea devolver otro tipo de contenido que "application / octet-stream" (por ejemplo, "image / gif").
Tu podrías intentar
httpResponseMessage.Content.Headers.Add("Content-Type", "application/octet-stream");