如何在ASP.NET WebAPI中返回一个文件(FileContentResult)

在一个常规的MVC控制器中,我们可以用FileContentResult输出pdf。

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

但是我们怎样才能把它变成一个ApiController呢?

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

这是我已经尝试过,但似乎没有工作。

 [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); } 

在浏览器中显示的返回结果是:

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

SO上还有一个类似的post: 在ASP.NET Web API中从控制器返回二进制文件 。 它谈到输出一个现有的文件。 但是我不能让它在一个stream中工作。

有什么build议么?

这是我发现的:

而不是作为Content返回StreamContent ,我可以使它与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; } 

如果你想返回IHttpActionResult你可以这样做:

 [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; } 

这个问题帮助我。

所以,试试这个:

控制器代码:

 [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; } 

查看Html标记(点击事件和简单的url):

 <script type="text/javascript"> $(document).ready(function () { $("#btn").click(function () { // httproute = "" - using this to construct proper web api links. window.location.href = "@Url.Action("GetFile", "Data", new { httproute = "" })"; }); }); </script> <button id="btn"> Button text </button> <a href=" @Url.Action("GetFile", "Data", new { httproute = "" }) ">Data</a> 

我不完全确定哪个部分是责怪,但这是为什么MemoryStream不适合你:

在写入MemoryStream ,它会增加它的Position属性。 StreamContent的构造函数考虑stream的当前Position 。 因此,如果您写入stream,然后将其传递给StreamContent ,则响应将从stream结束处的StreamContent开始。

有两种方法可以正确解决这个问题:

1)构build内容,写入stream

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

2)写入stream,重置位置,构build内容

 [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)看起来好一点,如果你有一个新的stream,1)更简单,如果你的stream不是从0开始