2012-03-02 123 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

相关问题