在ASP.NET Web API中从控制器返回二进制文件

我正在使用ASP.NET MVC的新WebAPI Web服务,将提供二进制文件,主要是.cab.exe文件。

下面的控制器方法似乎工作,这意味着它返回一个文件,但它的内容types设置为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")); } 

有没有更好的方法来做到这一点?

尝试使用一个简单的HttpResponseMessage并将其Content属性设置为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将为您处理stream。

  • 确保stream的当前位置设置为0(即stream的数据的开始)。 在上面的例子中,这是一个给定的,因为你只是打开文件。 但是,在其他情况下(例如,当你第一次写一些二进制数据到MemoryStream ),请确保stream.Seek(0, SeekOrigin.Begin); 或设置stream.Position = 0;

  • 通过文件stream,显式指定FileAccess.Read权限可以帮助防止Web服务器上的访问权限问题; IIS应用程序池帐户通常只能读取/列出/执行对wwwroot的访问权限。

对于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"/> 

虽然build议的解决scheme工作正常,还有另一种方法来从控制器返回一个字节数组,正确格式化响应stream:

  • 在请求中,设置头“Accept:application / octet-stream”。
  • 在服务器端,添加一个媒体types格式化程序来支持这种MIMEtypes。

不幸的是,WebApi没有包含“application / octet-stream”的格式化器。 在GitHub上有一个实现: BinaryMediaTypeFormatter (为了使它适用于webapi 2,改变了方法签名,有一些小改动)。

您可以将此格式化程序添加到您的全局configuration中:

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

如果请求指定了正确的Accept头,WebApi现在应该使用BinaryMediaTypeFormatter

我更喜欢这个解决scheme,因为返回byte []的动作控制器testing起来更加方便。 尽pipe如此,如果你想返回另外一个内容types而不是“application / octet-stream”(例如“image / gif”),另一个解决scheme允许你更多的控制。

您正在使用的重载设置序列化格式化程序的枚举。 您需要明确指定内容types,如下所示:

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

对于使用接受的答案中的方法下载相当大的文件时多次调用API的问题,请将响应缓冲设置为true System.Web.HttpContext.Current.Response.Buffer = true;

这确保了整个二进制内容在发送到客户端之前在服务器端被缓冲。 否则,你会看到多个请求被发送到控制器,如果你没有正确处理,文件将被损坏。

你可以试试

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