Cómo devolver un archivo (FileContentResult) en ASP.NET WebAPI

En un controlador MVC regular, podemos generar PDF con FileContentResult .

 public FileContentResult Test(TestViewModel vm) { var stream = new MemoryStream(); //... add content to the stream. return File(stream.GetBuffer(), "application/pdf", "test.pdf"); } 

Pero, ¿cómo podemos cambiarlo a un ApiController ?

 [HttpPost] public IHttpActionResult Test(TestViewModel vm) { //... return Ok(pdfOutput); } 

Esto es lo que he intentado, pero parece que no funciona.

 [HttpGet] public IHttpActionResult Test() { var stream = new MemoryStream(); //... var content = new StreamContent(stream); content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf"); content.Headers.ContentLength = stream.GetBuffer().Length; return Ok(content); } 

El resultado devuelto que se muestra en el navegador es:

 {"Headers":[{"Key":"Content-Type","Value":["application/pdf"]},{"Key":"Content-Length","Value":["152844"]}]} 

Y hay una publicación similar en SO: Devolver el archivo binario del controlador en ASP.NET Web API . Habla de salida un archivo existente. Pero no pude hacer que funcione con una transmisión.

¿Alguna sugerencia?

En lugar de devolver StreamContent como Content , puedo hacerlo funcionar con ByteArrayContent .

 [HttpGet] public HttpResponseMessage Generate() { var stream = new MemoryStream(); // processing the stream. var result = new HttpResponseMessage(HttpStatusCode.OK) { Content = new ByteArrayContent(stream.ToArray()) }; result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment") { FileName = "CertificationCard.pdf" }; result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); return result; } 

Si desea devolver IHttpActionResult , puede hacerlo así:

 [HttpGet] public IHttpActionResult Test() { var stream = new MemoryStream(); var result = new HttpResponseMessage(HttpStatusCode.OK) { Content = new ByteArrayContent(stream.GetBuffer()) }; result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment") { FileName = "test.pdf" }; result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); var response = ResponseMessage(result); return response; } 

Esta pregunta me ayudó.

Por lo tanto, intente esto:

Código del controlador:

 [HttpGet] public HttpResponseMessage Test() { var path = System.Web.HttpContext.Current.Server.MapPath("~/Content/test.docx");; HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK); var stream = new FileStream(path, FileMode.Open); result.Content = new StreamContent(stream); result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment"); result.Content.Headers.ContentDisposition.FileName = Path.GetFileName(path); result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); result.Content.Headers.ContentLength = stream.Length; return result; } 

Ver marcado de Html (con evento click y url simple):

   Data 

No estoy seguro de cuál es la parte culpable, pero esta es la razón por la cual MemoryStream no funciona para usted:

Al escribir en MemoryStream , incrementa su propiedad Position . El constructor de StreamContent tiene en cuenta la Position actual de la secuencia. Entonces, si escribe en la transmisión y luego la pasa a StreamContent , la respuesta comenzará desde la nada al final de la transmisión.

Hay dos formas de corregir esto correctamente:

1) construir contenido, escribir en la secuencia

 [HttpGet] public HttpResponseMessage Test() { var stream = new MemoryStream(); var response = Request.CreateResponse(HttpStatusCode.OK); response.Content = new StreamContent(stream); // ... // stream.Write(...); // ... return response; } 

2) escribir en la secuencia, restablecer la posición, construir el contenido

 [HttpGet] public HttpResponseMessage Test() { var stream = new MemoryStream(); // ... // stream.Write(...); // ... stream.Position = 0; var response = Request.CreateResponse(HttpStatusCode.OK); response.Content = new StreamContent(stream); return response; } 

2) parece un poco mejor si tienes un Stream nuevo, 1) es más simple si tu transmisión no comienza en 0

Para mí fue la diferencia entre

 var response = Request.CreateResponse(HttpStatusCode.OK, new StringContent(log, System.Text.Encoding.UTF8, "application/octet-stream"); 

y

 var response = Request.CreateResponse(HttpStatusCode.OK); response.Content = new StringContent(log, System.Text.Encoding.UTF8, "application/octet-stream"); 

El primero devolvía la representación JSON de StringContent: {“Headers”: [{“Key”: “Content-Type”, “Value”: [“application / octet-stream; charset = utf-8”]}]}

Mientras que el segundo devolvió el archivo propiamente dicho.

Parece que Request.CreateResponse tiene una sobrecarga que toma una cadena como el segundo parámetro y parece que esto fue lo que estaba causando que el objeto StringContent en sí se procesara como una cadena, en lugar del contenido real.