2012-03-02 124 views
266

我正在使用ASP.NET MVC的新WebAPI處理Web服務,該服務將提供二進制文件,主要是.cab.exe文件。在ASP.NET Web API中從控制器返回二進制文件

下面的控制器方法似乎工作,這意味着它返回一個文件,但它設置的內容類型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")); 
} 

有沒有更好的方式來做到這一點?

+0

任何想通過web api和IHTTPActionResult通過流返回字節數組的人都可以在這裏看到:http://nodogmablog.bryanhogan.net/2017/02/downloading-an-inmemory-file-using-web-api -2/ – IbrarMumtaz 2017-11-07 13:02:21

回答

432

嘗試使用簡單HttpResponseMessageContent屬性設置爲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; 
} 

有幾件事情需要注意使用的stream

  • 您不能致電stream.Dispose(),因爲Web API仍然需要能夠在處理控制器方法的result以將數據發送回客戶端時訪問它。因此,請勿使用using (var stream = …)塊。 Web API將爲您處理流。

  • 確保該流的當前位置設置爲0(即流的數據的開始)。在上面的例子中,這是一個給定的,因爲你只是剛剛打開文件。然而,在其他情況下(例如,當你第一次寫一些二進制數據到MemoryStream),確保stream.Seek(0, SeekOrigin.Begin);或設置stream.Position = 0;

  • 隨着文件流,顯式指定FileAccess.Read權限可以幫助防止Web服務器上的訪問權限問題; IIS應用程序池帳戶通常只能讀取/列出/執行wwwroot的訪問權限。

+32

您是否真的知道流何時關閉?我假設框架最終調用HttpResponseMessage.Dispose(),而HttpResponseMessage.Dispose()又調用HttpResponseMessage.Content.Dispose()有效地關閉流。 – 2012-04-20 18:36:59

+36

Steve - 你是對的,我通過向FileStream.Dispose添加斷點並運行此代碼進行驗證。框架調用HttpResponseMessage.Dispose,它調用StreamContent.Dispose,它調用FileStream.Dispose。 – 2012-08-22 20:02:44

+13

你真的不能添加一個'using'要麼結果('HttpResponseMessage')或流本身,因爲他們仍然在方法之外使用。正如@丹提到的,在完成向客戶端發送響應之後,它們被框架處置。 – carlosfigueira 2013-06-19 16:18:59

6

您正在使用的重載設置序列化格式化程序的枚舉。您需要明確指定內容類型,如:

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

感謝您的回覆。我試過了,我仍然在Fiddler中看到'Content Type:application/json'。如果我在返回'httpResponseMessage'響應之前中斷'Content Type',則看起來設置正確。還有什麼想法? – 2012-03-03 14:34:29

3

你可以嘗試

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

對於的Web API 2,可以實現IHttpActionResult。這裏是我的:

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

然後像這樣在你的控制器:

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

而且這裏有一個方法,你可以告訴IIS忽略的擴展名的請求,使該請求將使它的控制器:

<!-- web.config --> 
<system.webServer> 
    <modules runAllManagedModulesForAllRequests="true"/> 
+0

上述代碼是否適用於下載zip文件 – GowthamanSS 2014-01-13 10:28:33

+0

沒有抱歉,它不適用於zip文件。 – 2014-01-13 15:28:05

+0

@RonnieOverby:這是什麼意思: 「告訴IIS忽略具有擴展名的請求,以便請求將其發送給控制器」 什麼樣的請求具有擴展名,以及如何讓擴展名可能導致擴展名把它交給控制器? – 2014-02-19 00:24:52

8

儘管建議的解決方案工作得很好,還有另一種方式,返回來自控制器的字節數組,響應流格式正確:

  • 在請求中,設置標頭「Accept:application/octet-stream」。
  • 服務器端,添加一個媒體類型格式支持此MIME類型。

不幸的是,WebApi沒有包含任何「application/octet-stream」格式。在GitHub上有一個實現:BinaryMediaTypeFormatter(爲了使它適用於webapi 2,方法簽名已更改,有一些小修改)。

您可以添加此格式到全局配置:

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

的WebAPI現在應該使用BinaryMediaTypeFormatter如果請求指定了正確的Accept頭。

我喜歡這個解決方案,因爲動作控制器返回字節[]是更舒適的測試。儘管如此,如果您想返回另一種內容類型而不是「application/octet-stream」(例如「image/gif」),另一種解決方案允許您進行更多控制。

6

對於使用接受的答案中的方法下載相當大的文件時出現多次API調用問題的任何人,請將響應緩衝設置爲true System.Web.HttpContext.Current.Response.Buffer =真正;

這可以確保在整個二進制內容在服務器端緩存,然後發送給客戶端。否則,你會看到多個請求被髮送到控制器,如果你沒有正確處理它,文件將被損壞。

+1

緩衝區屬性[已被棄用](https://msdn.microsoft.com/en-us/library/system.web.httpresponse.buffer(v = vs.110).aspx)支持'BufferOutput '。它默認爲'true'。 – decates 2017-10-06 07:42:14

相關問題