为什么我的ASP.NET WebAPI文件处理程序不会在所有浏览器中返回文件?

时间:2014-05-14 18:45:53

标签: c# asp.net asp.net-web-api

我正在尝试从ASP.NET Web API方法返回Excel文件。我正在使用Web API作为我的ASP.NET Web表单应用程序的一部分。实际上,Web API方法可能会返回其他文件类型,但我现在正在尝试使用Excel文件进​​行稳定。

我能够跨用户代理访问ASP.NET Web API,但由于某种原因,我无法弄清楚如何让Web API成功将Excel文件返回给所有用户代理。出于某种原因,只有IE似乎能够解释HTTP响应并提供给定的请求的Excel文件。 Fiddler解释HTTP响应并识别出也返回了二进制文件。目前的代码如下:

public class FilesController : ApiController
{
    public HttpResponseMessage GetExcelFile(int id)
    {
        ExcelFile excelFile = new ExcelService().GetExcelFile(id);
        HttpResponseMessage result;
        if (excelFile == null)
        {
            result = new HttpResponseMessage(HttpStatusCode.NotFound);
            return result;
        }

        FileInfo fi = new FileInfo(
            string.Format("{0}/{1}/{2}",
            System.Web.Hosting.HostingEnvironment.MapPath("~/"),
            new ConfigUtilities().GetTemplateReportFilesRootPath(),
            excelFile.Name));

        if (!fi.Exists)
        {
            result = new HttpResponseMessage(HttpStatusCode.NotFound);
            return result;
        }

        long length = fi.Length;
        result = new HttpResponseMessage();
        result.StatusCode = HttpStatusCode.OK;

        var stream = new FileStream(fi.FullName, FileMode.Open, FileAccess.Read);
        MemoryStream memStream = new MemoryStream();
        stream.CopyTo(memStream);
        result.Content = new ByteArrayContent(memStream.ToArray());// new StreamContent(stream);

        result.Content.Headers.ContentType =
            new MediaTypeHeaderValue("application/vnd.ms-excel.sheet.macroEnabled.12");
        result.Content.Headers.ContentDisposition =
        new ContentDispositionHeaderValue("attachment")
        {
            FileName =
                String.Format(@"{0}", excelFile.DisplayName.Replace(@"""", "-"))
        };
        result.Content.Headers.ContentLength = length;

        return result;
    }
}

最初流式传输文件的源代码如下所示,但是我已经修改了几十次,几乎没有尝试返回文件的试错,所以它现在看起来像第一个代码块:

    result = new HttpResponseMessage(HttpStatusCode.OK);
    var stream = new FileStream(fi.FullName, FileMode.Open, FileAccess.Read);
    result.Content = new StreamContent(stream);
    result.Content.Headers.ContentType =
        new MediaTypeHeaderValue("application/octet-stream");
    result.Content.Headers.ContentDisposition =
    new ContentDispositionHeaderValue("attachment")
    {
        FileName =
            String.Format(@"{0}", excelFile.DisplayName.Replace(@"""", "-")),
        Size = fi.Length
    };
    result.Content.Headers.ContentLength = fi.Length;

    return result;

IE识别HTTP响应中的文件。 Chrome和FireFox虽然都有不同的反应。 Chrome如下:

Chrome example

FireFox如下:

enter image description here

我已经阅读了所有的stackoverflow线程和谷歌我能做的一切,但我似乎无法解决这个问题。我只是尝试从标准HTTP GET请求访问WebAPI,以便我可以通过网页上的标准HTTP超链接标记来提供文件。

Fiddler展示了以下内容:

enter image description here

以下是原始视图:

enter image description here

1 个答案:

答案 0 :(得分:2)

这是一个官方示例,用于显示正确的文件下载。您可以找到完整的示例here

行动结果:

public class OkFileDownloadResult : IHttpActionResult
{
    private readonly ApiController _controller;

    public OkFileDownloadResult(string localPath, string contentType, string downloadFileName,
        ApiController controller)
    {
        if (localPath == null)
        {
            throw new ArgumentNullException("localPath");
        }

        if (contentType == null)
        {
            throw new ArgumentNullException("contentType");
        }

        if (downloadFileName == null)
        {
            throw new ArgumentNullException("downloadFileName");
        }

        if (controller == null)
        {
            throw new ArgumentNullException("controller");
        }

        LocalPath = localPath;
        ContentType = contentType;
        DownloadFileName = downloadFileName;
        _controller = controller;
    }

    public string LocalPath { get; private set; }

    public string ContentType { get; private set; }

    public string DownloadFileName { get; private set; }

    public HttpRequestMessage Request
    {
        get { return _controller.Request; }
    }

    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        return Task.FromResult(Execute());
    }

    private HttpResponseMessage Execute()
    {
        HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
        response.Content = new StreamContent(File.OpenRead(MapPath(LocalPath)));
        response.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(ContentType);
        response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
        {
            FileName = DownloadFileName
        };
        return response;
    }

    private static string MapPath(string path)
    {
        // The following code is for demonstration purposes only and is not fully robust for production usage.
        // HttpContext.Current is not always available after asynchronous calls complete.
        // Also, this call is host-specific and will need to be modified for other hosts such as OWIN.
        return HttpContext.Current.Server.MapPath(path);
    }
}

ApiController的扩展方法

public static OkFileDownloadResult Download(this ApiController controller, string path, string contentType)
    {
        string downloadFileName = Path.GetFileName(path);
        return Download(controller, path, contentType, downloadFileName);
    }

    public static OkFileDownloadResult Download(this ApiController controller, string path, string contentType, string downloadFileName)
    {
        if (controller == null)
        {
            throw new ArgumentNullException("controller");
        }

        return new OkFileDownloadResult(path, contentType, downloadFileName, controller);
    }

在控制器中使用模式:

[Route("file")]
public OkFileDownloadResult GetFile()
{
    return this.Download("Download.xml", "application/xml");
}