Bagaimana cara mengembalikan file menggunakan API Web?

100

Saya menggunakan ASP.NET Web API .
Saya ingin mengunduh PDF dengan C # dari API (yang dihasilkan API).

Bisakah saya meminta API mengembalikan byte[]? dan untuk aplikasi C # bisa saya lakukan:

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

dan

File.WriteAllBytes()?
Kyle
sumber
"API Web"? Apa sebenarnya maksud Anda? Harap baca tinyurl.com/so-hints dan edit pertanyaan Anda.
Jon Skeet
1
@ JonSkeet: Web API adalah fitur baru di versi terbaru ASP.NET. Lihat asp.net/whitepapers/mvc4-release-notes#_Toc317096197
Robert Harvey
1
@Robert: Terima kasih - tag membuatnya lebih jelas, meskipun mengacu pada "API Web ASP.NET" masih akan lebih jelas. Sebagian kesalahan MS untuk nama yang terlalu umum juga :)
Jon Skeet
Siapa pun yang ingin mengembalikan streaming melalui api web dan IHTTPActionResult, lihat di sini: nodogmablog.bryanhogan.net/2017/02/…
IbrarMumtaz

Jawaban:

172

Lebih baik mengembalikan HttpResponseMessage dengan StreamContent di dalamnya.

Berikut contohnya:

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 dari komentar oleh patridge : Jika ada orang lain di sini yang ingin mengirimkan respons dari array byte alih-alih file sebenarnya, Anda akan ingin menggunakan ByteArrayContent (someData) baru daripada StreamContent (lihat di sini ).

Regfor
sumber
1
Hal pertama - kode ini akan menyebabkan pengecualian karena Anda baru saja membuat dua objek FileStream yang diarahkan ke file yang sama. Hal kedua adalah Anda tidak ingin menggunakan pernyataan "Using", karena segera setelah variabel keluar dari ruang lingkup, .NET akan membuangnya dan Anda akan mendapatkan pesan kesalahan tentang koneksi yang mendasarinya ditutup.
Brandon Montgomery
48
Jika ada orang lain di sini yang ingin mengirimkan respons dari array byte alih-alih file sebenarnya, Anda akan ingin menggunakan new ByteArrayContent(someData)alih-alih StreamContent(lihat di sini ).
patridge
Anda mungkin juga ingin mengganti base dispose () sehingga Anda bisa menangani sumber daya Anda dengan benar saat framework memanggilnya.
Phil Cooper
2
Saya ingin menunjukkan bahwa MediaTypeHeaderValue yang benar sangat penting dan untuk membuatnya dinamis jika Anda memiliki jenis file yang berbeda, Anda dapat melakukannya seperti ini. (dengan fileName adalah string dan memiliki tipe file yang diakhiri dengan .jpg, .pdf, docx dll ..) var contentType = MimeMapping.GetMimeMapping (fileName); response.Content.Headers.ContentType = new MediaTypeHeaderValue (contentType);
JimiSweden
1
Apakah FileStream dibuang secara otomatis?
Brian Tacker
37

Saya melakukan tindakan berikut:

[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);
    }
}
André de Mattos Ferraz
sumber
Ini sebenarnya menjawab pertanyaan
Mick
1
Ini bukan ide yang bagus dengan file besar karena memuat seluruh gambar ke dalam memori. Opsi aliran lebih baik.
Paul Reedy
@PaulReedy Perfect ... tetapi dalam banyak kasus, Anda tidak perlu berurusan dengan file besar. Tapi saya sangat setuju dengan maksud Anda!
André de Mattos Ferraz
15

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

Jika Anda tidak ingin mengunduh PDF dan menggunakan browser bawaan penampil PDF, hapus dua baris berikut:

responseMsg.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
responseMsg.Content.Headers.ContentDisposition.FileName = file.FileName;
Ogglas
sumber
@ElbertJohnFelipe Ya, Anda mendapatkan file dengan var file = GetFile(id);. file.SomeDataadalah Byte Array ( byte[]) dan file.FileNamemerupakan string.
Ogglas
Terima kasih atas kiriman Anda. 'HttpResponseMessage' tidak berfungsi untuk saya di dalam ApiController, jadi Anda menyelamatkan saya.
Maksimal
14

Sekadar catatan untuk .Net Core: Kita bisa menggunakan FileContentResultdan menyetel contentType ke application/octet-streamjika kita ingin mengirim byte mentah. Contoh:

[HttpGet("{id}")]
public IActionResult GetDocumentBytes(int id)
{
    byte[] byteArray = GetDocumentByteArray(id);
    return new FileContentResult(byteArray, "application/octet-stream");
}
Amir Shirazi
sumber
1
Ini berfungsi dengan baik, Juga jika Anda ingin mengontrol nama file, ada properti yang FileContentResultdipanggil FileDownloadNameuntuk menentukan nama file
weeksdev
@weeksdev ah tidak tahu itu. Terima kasih atas komentarnya.
Amir Shirazi
Itu saja, terima kasih. Juga komentar dari weeksdev sangat berguna.
fragg
1

Saya bertanya-tanya apakah ada cara sederhana untuk mendownload file dengan cara yang lebih ... "umum". Saya datang dengan ini.

Ini sederhana ActionResultyang akan memungkinkan Anda mengunduh file dari panggilan pengontrol yang mengembalikan file IHttpActionResult. File tersebut disimpan di byte[] Content. Anda dapat mengubahnya menjadi aliran jika perlu.

Saya menggunakan ini untuk mengembalikan file yang disimpan dalam kolom varbinary database.

    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);
        }
    }
Jake
sumber
Penjelasan singkat tentang bagaimana kode Anda memperbaiki masalah OP akan meningkatkan kualitas jawaban Anda.
Adrian Mole