我正在使用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"/>

其他回答

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

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

您可以尝试下面的代码片段

httpResponseMessage.Content.Headers.Add("Content-Type", "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"/>

你可以试试

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

虽然建议的解决方案工作得很好,但还有另一种方法可以从控制器返回一个字节数组,并正确格式化响应流:

在请求中,设置报头“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”),另一种解决方案允许你进行更多的控制。