如何使用Web API返回文件?


98

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

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

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

File.WriteAllBytes()?

“ Web API”?你到底什么意思?请阅读tinyurl.com/so-hints并编辑您的问题。
乔恩·斯基特

1
@JonSkeet:Web API是最新版本的ASP.NET中的一项新功能。参见asp.net/whitepapers/mvc4-release-notes#_Toc317096197
Robert Harvey

1
@Robert:谢谢-标记使它更清晰,尽管对“ ASP.NET Web API”的指称仍然更加清晰。MS的过错也是通用名称的错误:)
Jon Skeet


任何想要通过Web api和IHTTPActionResult返回流的人都可以在这里查看:nodogmablog.bryanhogan.net/2017/02/…–
IbrarMumtaz

Answers:


170

最好返回带有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的评论:如果其他任何人希望从字节数组而不是实际文件中发送响应,您将要使用新的ByteArrayContent(someData)而不是StreamContent(请参见此处))。


1
第一件事-此代码将导致异常,因为您要更新指向同一文件的两个FileStream对象。第二件事是您不希望使用“使用”语句,因为一旦变量超出范围,.NET就会立即处理它,并且您会收到有关关闭基础连接的错误消息。
布兰登·蒙哥马利

48
如果其他任何人希望从字节数组而不是实际文件中发送响应,则您将要使用new ByteArrayContent(someData)而不是StreamContent(请参阅此处)。
patridge

您可能还想覆盖基本的dispose(),以便在框架调用它时可以正确处理资源。
Phil Cooper 2014年

1
我想指出,正确的MediaTypeHeaderValue是至关重要的,如果您具有不同的文件类型,则可以使其动态化,您可以这样做。(其中fileName是字符串,并且文件类型以.jpg,.pdf,docx等结尾。)var contentType = MimeMapping.GetMimeMapping(fileName); response.Content.Headers.ContentType =新的MediaTypeHeaderValue(contentType);
JimiSweden '16

1
FileStream是否会自动处置?
Brian Tacker

37

我进行了以下操作:

[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);
    }
}

这实际上回答了问题
Mick

1
对于大文件,这不是一个好主意,因为它将整个图像加载到内存中。流选项更好。
保罗·里迪

@PaulReedy完美...但是在很多情况下,您不需要处理大型文件。但是我完全同意你的观点!
安德烈·德·马托斯·菲拉兹

14

IHttpActionResultin中的示例ApiController

[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 Array(byte[])并且file.FileNamestring
Ogglas

谢谢你的文章。'HttpResponseMessage'在ApiController中对我不起作用,所以您救了我。
最多

13

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

1
这个伟大的工程,另外,如果你想控制文件的名字里有一个物业FileContentResult称为FileDownloadName指定文件名
weeksdev

@weeksdev啊不知道。感谢您的评论。
阿米尔·西拉齐

就是这样,谢谢。另外,weeksdev的评论非常有用。
fragg

1

我一直在想,是否有一种简单的方法可以以更多……“通用”的方式下载文件。我想到了这个。

这很简单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的问题将提高答案的质量。
阿德里安·摩尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.