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

其他回答

对于使用。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的回复

对于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");

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

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

尝试使用一个简单的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.