从 ASP.NET Web API 中的控制器返回二进制文件

问题描述 投票:0回答:9

我正在使用 ASP.NET MVC 的新 WebAPI 开发一个 Web 服务,该服务将提供二进制文件,主要是

.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"));
}

有更好的方法吗?

asp.net asp.net-mvc asp.net-web-api
9个回答
543
投票

尝试使用简单的

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 将为您处理流。

  • 确保流的当前位置设置为 0(即流数据的开头)。在上面的示例中,这是给定的,因为您刚刚打开了该文件。但是,在其他情况下(例如当您第一次将一些二进制数据写入

    MemoryStream
    时),请确保
    stream.Seek(0, SeekOrigin.Begin);
    或设置
    stream.Position = 0;

  • 对于文件流,显式指定

    FileAccess.Read
    权限可以帮助防止 Web 服务器上的访问权限问题; IIS 应用程序池帐户通常仅被授予对 wwwroot 的读取/列出/执行访问权限。


146
投票

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

38
投票

对于那些使用 .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 类型和扩展名将取决于个人需求。

参考:

SO Post Answer

by @NKosi


10
投票

在请求中,设置标头“Accept:application/octet-stream”。
  • 服务器端,添加媒体类型格式化程序以支持此 mime 类型。
  • 不幸的是,WebApi 不包含任何“application/octet-stream”的格式化程序。 GitHub 上有一个实现:
BinaryMediaTypeFormatter

(进行了一些小的调整以使其适用于 webapi 2,方法签名已更改)。 您可以将此格式化程序添加到您的全局配置中:

HttpConfiguration config; // ... config.Formatters.Add(new BinaryMediaTypeFormatter(false));

如果请求指定了正确的 Accept 标头,
WebApi 现在应该使用 
BinaryMediaTypeFormatter


我更喜欢这个解决方案,因为返回 byte[] 的动作控制器更容易测试。不过,如果您想返回除“application/octet-stream”之外的其他内容类型(例如“image/gif”),另一种解决方案允许您进行更多控制。


9
投票

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


8
投票

httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");



5
投票

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



0
投票

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

希望它对你有用。


0
投票

[HttpGet] public IActionResult Get() { string filename = "C:\\Data\\yourprog.exe"; var fileBytes = System.IO.File.ReadAllBytes(filename); return File(fileBytes, "application/octet-stream", "yourprog.exe"); }

	
© www.soinside.com 2019 - 2024. All rights reserved.