我正在使用ASP开发一个web服务。NET MVC的新WebAPI将提供二进制文件,主要是。cab和。exe文件。
下面的控制器方法似乎可以工作,这意味着它返回了一个文件,但它将内容类型设置为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"));
}
还有更好的办法吗?
对于Web API 2,您可以实现IHttpActionResult。这是我的:
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
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"/>
对于Web API 2,您可以实现IHttpActionResult。这是我的:
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
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"/>
虽然建议的解决方案工作得很好,但还有另一种方法可以从控制器返回一个字节数组,并正确格式化响应流:
在请求中,设置报头“Accept: application/octet-stream”。
服务器端,添加一个媒体类型格式化器来支持此mime类型。
不幸的是,WebApi不包括任何用于“应用程序/八字节流”的格式化程序。在GitHub上有一个实现:BinaryMediaTypeFormatter(有一些小的调整,使其适用于webapi 2,方法签名更改)。
你可以在全局配置中添加这个格式化器:
HttpConfiguration config;
// ...
config.Formatters.Add(new BinaryMediaTypeFormatter(false));
如果请求指定了正确的Accept报头,WebApi现在应该使用BinaryMediaTypeFormatter。
我更喜欢这个解决方案,因为动作控制器返回字节[]更适合测试。不过,如果你想返回“application/octet-stream”以外的其他内容类型(例如“image/gif”),另一种解决方案允许你进行更多的控制。