Web API kullanarak bir dosya nasıl iade edilir?


101

ASP.NET Web API kullanıyorum .
API'den (API'nin ürettiği) C # içeren bir PDF indirmek istiyorum.

API’nin a döndürmesini sağlayabilir miyim byte[]? ve C # uygulaması için şunları yapabilir miyim:

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

ve

File.WriteAllBytes()?

"Web API"? Tam olarak ne demek istiyorsun? Lütfen tinyurl.com/so-hints adresini okuyun ve sorunuzu düzenleyin.
Jon Skeet

1
@JonSkeet: Web API, ASP.NET'in en son sürümündeki yeni bir özelliktir. Bkz asp.net/whitepapers/mvc4-release-notes#_Toc317096197
Robert Harvey

1
@Robert: Teşekkürler - etiketi daha anlaşılır hale getiriyor, ancak "ASP.NET Web API" ye atıfta bulunmak daha da net olurdu. Kısmen MS'in saçma bir genel isim için de hatası :)
Jon Skeet


Akışı web api ve IHTTPActionResult aracılığıyla geri göndermek isteyenler buraya bakın: nodogmablog.bryanhogan.net/2017/02/…
IbrarMumtaz

Yanıtlar:


173

İçinde StreamContent bulunan HttpResponseMessage'ı döndürmek daha iyidir.

İşte örnek:

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

Patridge'in yorumundan UPD : Buraya gerçek bir dosya yerine bir bayt dizisinden yanıt göndermek isteyen biri gelirse , StreamContent yerine yeni ByteArrayContent (someData) kullanmak isteyeceksiniz ( buraya bakın ).


1
İlk şey - bu kod, aynı dosyaya işaret edilmiş iki FileStream nesnesini yeni oluşturduğunuz için bir istisnaya neden olacaktır. İkinci olarak, "Using" deyimini kullanmak istememenizdir, çünkü değişken kapsam dışına çıktığı anda .NET onu atar ve kapatılan temel bağlantıyla ilgili hata mesajları alırsınız.
Brandon Montgomery

48
Buraya gerçek bir dosya yerine bir bayt dizisinden yanıt göndermek isteyen biri gelirse, new ByteArrayContent(someData)bunun yerine kullanmak isteyeceksiniz StreamContent( buraya bakın ).
patridge

Ayrıca, temel dispose () işlevini geçersiz kılmak isteyebilirsiniz, böylece çerçeve çağırdığında kaynaklarınızı doğru şekilde yönetebilirsiniz.
Phil Cooper

2
Doğru MediaTypeHeaderValue değerinin çok önemli olduğunu ve farklı dosya türleriniz varsa bunu dinamik hale getirmek için bunu yapabileceğinizi belirtmek isterim. (burada dosyaAdı bir dizedir ve .jpg, .pdf, docx vb. gibi biten bir dosya türüne sahiptir) var contentType = MimeMapping.GetMimeMapping (fileName); response.Content.Headers.ContentType = new MediaTypeHeaderValue (contentType);
JimiSweden

1
FileStream otomatik olarak atılıyor mu?
Brian Tacker

37

Takip et eylemini yaptım:

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

Bu aslında soruyu yanıtlıyor
Mick

1
Tüm görüntüyü belleğe yüklediğinden, bu büyük dosyalarda iyi bir fikir olmaz. Akış seçeneği daha iyidir.
Paul Reedy

@PaulReedy Perfect ... ancak çoğu durumda büyük dosyalarla uğraşmanıza gerek yoktur. Ama fikrinize tamamen katılıyorum!
André de Mattos Ferraz

16

Örnek IHttpActionResultolarak 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'yi indirmek ve PDF görüntüleyicide yerleşik bir tarayıcı kullanmak istemiyorsanız, aşağıdaki iki satırı kaldırın:

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

@ElbertJohnFelipe Evet, dosyayı ile alıyorsunuz var file = GetFile(id);. file.SomeDataBir bayt dizisi (bir byte[]) ve file.FileNamebir string.
Ogglas

Gönderiniz için teşekkürler. 'HttpResponseMessage' bir ApiController içinde benim için çalışmadı, bu yüzden beni kurtardın.
Maksimum

16

Sadece bir not .Net Core: Ham baytları göndermek istiyorsak FileContentResultcontentType'ı kullanabilir ve ayarlayabiliriz application/octet-stream. Misal:

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

1
Bu harika çalışıyor, Ayrıca dosya adını kontrol etmek istiyorsanız, dosya adını belirtmek için FileContentResultçağrılan bir özellik varFileDownloadName
weekdev

@weeksdev ah bunu bilmiyordu. Yorum için teşekkürler.
Amir Shirazi

İşte bu, teşekkürler. Ayrıca weekdev'den gelen yorumlar çok yararlıdır.
fragg

1

Bir dosyayı daha "genel" bir şekilde indirmenin basit bir yolu olup olmadığını merak ediyordum. Bunu ben buldum.

Bu ActionResult, bir denetleyici çağrısından bir IHttpActionResult. Dosya byte[] Content,. Gerekirse onu bir akışa dönüştürebilirsiniz.

Bunu bir veritabanının varbinary sütununda depolanan dosyaları döndürmek için kullandım.

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

Kodunuzun OP'nin problemlerini nasıl düzelttiğine dair kısa bir açıklama yanıtınızın kalitesini artıracaktır.
Adrian Mole

0

Dosyayı indirmenin başka bir yolu, akış içeriğini doğrudan yanıtın gövdesine yazmaktır:

[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();           
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.