在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");