2014-09-25 1224 views
103

在常規的MVC控制器中,我們可以使用FileContentResult輸出pdf。如何在ASP.NET WebAPI中返回文件(FileContentResult)

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:Returning binary file from controller in ASP.NET Web API 。它討論輸出一個現有的文件。但是我無法讓它在一個流中工作。

有什麼建議嗎?

+1

這篇文章幫助了我:http://stackoverflow.com/a/23768883/585552 – Greg 2015-09-23 00:48:29

回答

128

,而不是返回StreamContent作爲Content的,我可以使它與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; 
} 
+2

如果上半部分回答你的問題,請僅作爲回答發佈。下半場似乎是一個不同的問題 - 爲此發佈一個新問題。 – gunr2171 2014-12-15 18:21:28

+1

您是否找到解決方案? – mayabelle 2015-02-18 18:43:42

+3

嗨,感謝分享,有一個簡單的問題(我猜)。我有一個接收httpresponse消息的C#前端。如何提取流內容並使其可用,以便用戶可以將其保存到磁盤或某些內容(並且我可以獲取實際文件)?謝謝! – Ronald 2015-08-24 10:06:35

32

This問題幫助我。

所以,試試這個:

控制器代碼:

[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標記(與click事件和簡單的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> 
+0

在這裏,您正在使用'FileStream'對於服務器上的現有文件,它與'MemoryStream'有點不同,但是感謝輸入 – Blaise 2014-09-25 14:26:08

+3

如果您從Web服務器上的文件讀取數據,請確保使用FileShare.Read過載,否則您可能會遇到使用中的文件異常。 – 2014-09-25 14:34:16

+0

如果你用內存流代替它將無法工作? – aleha 2014-09-25 14:34:43

7

我不是很確定責怪哪一部分,但在這裏就是爲什麼MemoryStream不會爲你工作:

當你寫MemoryStream,它增加它的Position財產。 StreamContent的構造函數考慮到流的當前Position。因此,如果您寫入流,然後將其傳遞到StreamContent,則響應將從流末尾的無效開始。

有兩種方法妥善解決這個問題:

1)構建的內容,寫入流

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

2)寫入流,復位位置,建設內容

[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 )看起來好一點,如果你有一個新的流,1)更簡單,如果你的流不從0開始

+0

此代碼實際上並未提供任何解決方案,因爲它使用與問題中提到的方法相同的方法。這個問題已經表明,這不起作用,我可以證實。返回Ok(新的StreamContent(stream))返回StreamContent的JSON表示。 – 2016-07-27 19:47:13

+0

更新了代碼。這個答案實際上回答了'爲什麼簡單的解決方案使用FileStream而不是MemoryStream'而不是如何在WebApi中返回文件這個更微妙的問題。 – 2016-07-28 01:05:15

45

如果你想返回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; 
} 
+2

好的更新來顯示IHttpActionResult返回類型。此代碼的重構將移動調用一個自定義的IHttpActionResult,如下面列出的那個:http://stackoverflow.com/questions/23768596/why-is-my-file-not-being-returned-bya-a- get-request-from-my-web-api-function – Josh 2016-05-26 21:19:32

+0

這篇文章演示了一個很好的單一使用實現。就我而言,上述鏈接中列出的幫助方法證明更有幫助 – hanzolo 2017-03-29 22:59:51

0

對我來說是

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

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

第一個被返回的區別StringContent的JSON表示:{「Headers」:[{「Key」:「Content-Type」,「Value」:[「application/octet-stream;字符集= UTF-8" ]}]}

而第二個被返回文件正確。

似乎Request.CreateResponse具有重載採用字符串作爲第二個參數,這似乎有是什麼導致StringContent對象本身呈現爲一個字符串,而不是實際的內容。