【问题标题】:How to return a file using Web API?如何使用 Web API 返回文件?
【发布时间】:2012-06-22 22:37:16
【问题描述】:

我正在使用 ASP.NET Web API
我想从 API(API 生成)下载带有 C# 的 PDF。

我可以让 API 返回一个 byte[] 吗?对于 C# 应用程序,我可以这样做:

byte[] pdf = client.DownloadData("urlToAPI");? 

File.WriteAllBytes()?

【问题讨论】:

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


【解决方案1】:

最好返回带有 StreamContent 的 HttpResponseMessage。

示例如下:

public HttpResponseMessage GetFile(string id)
{
    if (String.IsNullOrEmpty(id))
        return Request.CreateResponse(HttpStatusCode.BadRequest);

    string fileName;
    string localFilePath;
    int fileSize;

    localFilePath = getFileFromID(id, out fileName, out fileSize);

    HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
    response.Content = new StreamContent(new FileStream(localFilePath, FileMode.Open, FileAccess.Read));
    response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
    response.Content.Headers.ContentDisposition.FileName = fileName;
    response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");

    return response;
}

UPD来自patridge的评论: 如果其他人来到这里希望从字节数组而不是实际文件发送响应,您将需要使用 new ByteArrayContent(someData) 而不是 StreamContent(请参阅here)。

【讨论】:

  • 第一件事 - 此代码将导致异常,因为您正在新建两个指向同一文件的 FileStream 对象。第二件事是您不想使用“Using”语句,因为一旦变量超出范围,.NET 就会处理它,并且您会收到有关底层连接被关闭的错误消息。
  • 如果其他人来到这里希望从字节数组而不是实际文件发送响应,您将要使用 new ByteArrayContent(someData) 而不是 StreamContent(参见 here )。
  • 您可能还想覆盖基本 dispose() 以便在框架调用它时正确处理您的资源。
  • 我想指出正确的 MediaTypeHeaderValue 是至关重要的,如果你有不同的文件类型,你可以这样做。 (其中 fileName 是一个字符串,文件类型以 .jpg、.pdf、docx 等结尾。) var contentType = MimeMapping.GetMimeMapping(fileName); response.Content.Headers.ContentType = new MediaTypeHeaderValue(contentType);
  • FileStream 会自动处理吗?
【解决方案2】:

我做了以下动作:

[HttpGet]
[Route("api/DownloadPdfFile/{id}")]
public HttpResponseMessage DownloadPdfFile(long id)
{
    HttpResponseMessage result = null;
    try
    {
        SQL.File file = db.Files.Where(b => b.ID == id).SingleOrDefault();

        if (file == null)
        {
            result = Request.CreateResponse(HttpStatusCode.Gone);
        }
        else
        {
            // sendo file to client
            byte[] bytes = Convert.FromBase64String(file.pdfBase64);


            result = Request.CreateResponse(HttpStatusCode.OK);
            result.Content = new ByteArrayContent(bytes);
            result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
            result.Content.Headers.ContentDisposition.FileName = file.name + ".pdf";
        }

        return result;
    }
    catch (Exception ex)
    {
        return Request.CreateResponse(HttpStatusCode.Gone);
    }
}

【讨论】:

  • 这实际上回答了问题
  • 这对于大文件来说不是一个好主意,因为它将整个图像加载到内存中。流选项更好。
  • @PaulReedy Perfect...但在很多情况下,您不需要处理大文件。但我完全同意你的观点!
【解决方案3】:

请注意.Net Core:如果我们想发送原始字节,我们可以使用FileContentResult 并将contentType 设置为application/octet-stream。示例:

[HttpGet("{id}")]
public IActionResult GetDocumentBytes(int id)
{
    byte[] byteArray = GetDocumentByteArray(id);
    return new FileContentResult(byteArray, "application/octet-stream");
}

【讨论】:

  • 这很好用,另外,如果你想控制文件名,FileContentResult 上有一个名为 FileDownloadName 的属性来指定文件名
  • @weeksdev 啊不知道。感谢您的评论。
  • 就是这样,谢谢。来自 weekdev 的评论也非常有用。
【解决方案4】:

ApiController 中的IHttpActionResult 示例。

[HttpGet]
[Route("file/{id}/")]
public IHttpActionResult GetFileForCustomer(int id)
{
    if (id == 0)
      return BadRequest();

    var file = GetFile(id);

    IHttpActionResult response;
    HttpResponseMessage responseMsg = new HttpResponseMessage(HttpStatusCode.OK);
    responseMsg.Content = new ByteArrayContent(file.SomeData);
    responseMsg.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
    responseMsg.Content.Headers.ContentDisposition.FileName = file.FileName;
    responseMsg.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
    response = ResponseMessage(responseMsg);
    return response;
}

如果您不想下载 PDF 并使用内置 PDF 查看器的浏览器,请删除以下两行:

responseMsg.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
responseMsg.Content.Headers.ContentDisposition.FileName = file.FileName;

【讨论】:

  • @ElbertJohnFelipe 是的,你会得到带有var file = GetFile(id); 的文件。 file.SomeData 是字节数组 (byte[]),file.FileNamestring
  • 感谢您的帖子。 'HttpResponseMessage' 在 ApiController 中对我不起作用,所以你救了我。
【解决方案5】:

我一直想知道是否有一种简单的方法可以以更...“通用”的方式下载文件。我想出了这个。

这是一个简单的ActionResult,允许您从返回IHttpActionResult 的控制器调用下载文件。 该文件存储在byte[] Content 中。如果需要,您可以将其转换为流。

我用它来返回存储在数据库的 varbinary 列中的文件。

    public class FileHttpActionResult : IHttpActionResult
    {
        public HttpRequestMessage Request { get; set; }

        public string FileName { get; set; }
        public string MediaType { get; set; }
        public HttpStatusCode StatusCode { get; set; }

        public byte[] Content { get; set; }

        public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {
            HttpResponseMessage response = new HttpResponseMessage(StatusCode);

            response.StatusCode = StatusCode;
            response.Content = new StreamContent(new MemoryStream(Content));
            response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
            response.Content.Headers.ContentDisposition.FileName = FileName;
            response.Content.Headers.ContentType = new MediaTypeHeaderValue(MediaType);

            return Task.FromResult(response);
        }
    }

【讨论】:

  • 简要说明您的代码如何解决 OP 的问题将提高您的回答质量。
【解决方案6】:

你可以试试,HttpClient 从另一端下载文件,同时你可以作为文件结果传递

 [HttpGet]
    [Route("api/getFile")]
    public async  Task<FileResult> GetFile(string Param1,string Param2)
    {
        try
        {
            Stream stream = null;
            string strURL = @"File URL";
            HttpClient client = new HttpClient();
            HttpResponseMessage httpResponse = await client.GetAsync(strURL);
            Stream streamToReadFrom = await httpResponse.Content.ReadAsStreamAsync();
            return File(streamToReadFrom, "{MIME TYPE}");

        }
        catch (Exception ex)
        {

            throw ex;
        }
        finally
        { 
        
        }
    }

【讨论】:

    【解决方案7】:

    另一种下载文件的方法是将流内容直接写入响应的正文:

    [HttpGet("pdfstream/{id}")]
    public async Task  GetFile(long id)
    {        
        var stream = GetStream(id);
        Response.StatusCode = (int)HttpStatusCode.OK;
        Response.Headers.Add( HeaderNames.ContentDisposition, $"attachment; filename=\"{Guid.NewGuid()}.pdf\"" );
        Response.Headers.Add( HeaderNames.ContentType, "application/pdf"  );            
        await stream.CopyToAsync(Response.Body);
        await Response.Body.FlushAsync();           
    }
    

    【讨论】:

      猜你喜欢
      • 2013-03-21
      • 2016-04-23
      • 2015-07-09
      • 2020-06-13
      • 2016-12-17
      • 1970-01-01
      • 2018-07-17
      • 1970-01-01
      • 2016-04-23
      相关资源
      最近更新 更多