Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/asp.net/37.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在ASP.NET Web API中从控制器返回二进制文件_Asp.net_Asp.net Mvc_Asp.net Web Api - Fatal编程技术网

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

在ASP.NET Web API中从控制器返回二进制文件,asp.net,asp.net-mvc,asp.net-web-api,Asp.net,Asp.net Mvc,Asp.net Web Api,我正在使用ASP.NETMVC的新WebAPI开发一个web服务,它将提供二进制文件,主要是.cab和.exe文件 以下控制器方法似乎有效,这意味着它返回一个文件,但它将内容类型设置为application/json: 有更好的方法吗?您正在使用的重载设置序列化格式化程序的枚举。您需要明确指定内容类型,如: httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-s

我正在使用ASP.NETMVC的新WebAPI开发一个web服务,它将提供二进制文件,主要是.cab和.exe文件

以下控制器方法似乎有效,这意味着它返回一个文件,但它将内容类型设置为application/json:


有更好的方法吗?

您正在使用的重载设置序列化格式化程序的枚举。您需要明确指定内容类型,如:

httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
尝试使用简单的,其内容属性设置为:

关于使用的流,需要注意以下几点:

您不能调用stream.Dispose,因为Web API在处理控制器方法的结果以将数据发送回客户端时仍需要能够访问它。因此,不要使用using var stream=…块。Web API将为您处理流

确保流的当前位置设置为0,即流数据的开始位置。在上面的示例中,这是给定的,因为您刚刚打开了文件。但是,在其他情况下,例如当您第一次将一些二进制数据写入MemoryStream时,请确保stream.Seek0、SeekOrigin.Begin;或设置流。位置=0

对于文件流,明确指定权限有助于防止web服务器上的访问权限问题;IIS应用程序池帐户通常只被授予对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);
    }
}
这里有一种方法可以告诉IIS忽略带有扩展名的请求,这样请求就会发送到控制器:

[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);
}
<!-- web.config -->
<system.webServer>
  <modules runAllManagedModulesForAllRequests="true"/>

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

在请求中,设置header Accept:application/octet stream。 服务器端,添加媒体类型格式化程序以支持此mime类型。 不幸的是,WebApi不包括任何应用程序/八位字节流的格式化程序。在GitHub上有一个实现:为了使其适用于webapi 2,进行了一些小的修改,方法签名发生了变化

您可以将此格式化程序添加到全局配置中:

HttpConfiguration config;
// ...
config.Formatters.Add(new BinaryMediaTypeFormatter(false));
如果请求指定了正确的Accept头,WebApi现在应该使用BinaryMediaTypeFormatter


我更喜欢这种解决方案,因为返回字节[]的动作控制器更易于测试。尽管如此,如果您想返回应用程序/八位字节流以外的另一种内容类型,例如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");

希望它对您有用。

谢谢您的回复。我试过了,但我仍然在Fiddler中看到内容类型:application/json。如果在返回httpResponseMessage响应之前中断,则内容类型似乎设置正确。还有什么想法吗?你知道小溪什么时候关闭吗?我假设框架最终会调用HttpResponseMessage.Dispose,而HttpResponseMessage.Content.Dispose又会调用HttpResponseMessage.Content.Dispose,从而有效地关闭流。Steve-您是正确的,我通过向FileStream.Dispose添加断点并运行此代码进行了验证。该框架调用HttpResponseMessage.Dispose,后者调用StreamContent.Dispose,后者调用FileStream.Dispose。您不能向结果HttpResponseMessage或流本身添加using,因为它们仍将在方法外部使用。正如@Dan所提到的,在框架将响应发送给客户端之后,它们将由框架处理。@B.ClayShannon是的,就是这样。就客户端而言,它只是HTTP响应内容中的一堆字节。客户端可以随意处理这些字节,包括将其保存到本地文件。@carlosfigueira,您好,您知道如何在发送完所有字节后删除该文件吗?回答不错,并非总是这样,粘贴后代码会运行,对于不同的情况,会运行不同的文件。@JonyAdamit谢谢。我想再来一次
最重要的是在方法签名上放置一个异步修饰符,并完全删除任务的创建:这只是一个提醒,任何人都可以使用这个正在运行的IIS7+。runAllManagedModulesForAllRequests现在可以运行了。@BendEg好像有一次我检查了源代码,它检查了。应该这样做是有道理的。由于无法控制框架的源代码,这个问题的任何答案都可能随着时间的推移而改变。实际上已经有一个内置的FileResult甚至FileStreamResult类。Buffer属性支持BufferOutput。默认为true。任何人想要通过web api和IHTTPActionResult通过流返回字节数组,请参见此处:只需注意,如果它是一个图像,并且您希望它可以在具有直接URL访问的浏览器中查看,则不提供文件名。
    [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");
    }
httpResponseMessage.Content.Headers.Add("Content-Type", "application/octet-stream");