我正在使用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"));
}

还有更好的办法吗?


您正在使用的重载设置序列化格式化程序的枚举。你需要像这样显式地指定内容类型:

httpResponseMessage.Content.Headers.ContentType = 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;
}

关于使用的流有几点需要注意:

You must not call stream.Dispose(), since Web API still needs to be able to access it when it processes the controller method's result to send data back to the client. Therefore, do not use a using (var stream = …) block. Web API will dispose the stream for you. Make sure that the stream has its current position set to 0 (i.e. the beginning of the stream's data). In the above example, this is a given since you've only just opened the file. However, in other scenarios (such as when you first write some binary data to a MemoryStream), make sure to stream.Seek(0, SeekOrigin.Begin); or set stream.Position = 0; With file streams, explicitly specifying FileAccess.Read permission can help prevent access rights issues on web servers; IIS application pool accounts are often given only read / list / execute access rights to the wwwroot.

你可以试试

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

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

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

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

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

对于使用。net Core的用户:

你可以在API控制器方法中使用IActionResult接口,就像这样。

[HttpGet("GetReportData/{year}")]
public async Task<IActionResult> GetReportData(int year)
{
    // Render Excel document in memory and return as Byte[]
    Byte[] file = await this._reportDao.RenderReportAsExcel(year);

    return File(file, "application/vnd.openxmlformats", "fileName.xlsx");
}

这个例子很简单,但应该能让人理解。在。net Core中,这个过程比以前的。net版本要简单得多——即不需要设置响应类型、内容、报头等。

当然,文件和扩展名的MIME类型也取决于个人需求。

参考:@NKosi的回复

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

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

希望它对你有用。