Bagaimana cara mengunduh file dengan Angular2 atau lebih tinggi


181

Saya memiliki aplikasi WebApi / MVC yang saya kembangkan klien angular2 (untuk menggantikan MVC). Saya mengalami beberapa masalah memahami bagaimana Angular menyimpan file.

Permintaannya ok (berfungsi baik dengan MVC, dan kami dapat mencatat data yang diterima) tapi saya tidak tahu bagaimana cara menyimpan data yang diunduh (saya kebanyakan mengikuti logika yang sama seperti pada posting ini ). Saya yakin itu sangat sederhana, tapi sejauh ini saya tidak mengerti.

Kode fungsi komponen di bawah ini. Saya sudah mencoba berbagai alternatif, cara gumpalan harus menjadi cara untuk pergi sejauh yang saya mengerti, tetapi tidak ada fungsi createObjectURLdi dalamnya URL. Saya bahkan tidak dapat menemukan definisi URLdi jendela, tetapi ternyata itu ada. Jika saya menggunakan FileSaver.jsmodul saya mendapatkan kesalahan yang sama. Jadi saya kira ini adalah sesuatu yang berubah baru-baru ini atau belum diimplementasikan. Bagaimana saya bisa memicu penyimpanan file dalam A2?

downloadfile(type: string){

    let thefile = {};
    this.pservice.downloadfile(this.rundata.name, type)
        .subscribe(data => thefile = new Blob([data], { type: "application/octet-stream" }), //console.log(data),
                    error => console.log("Error downloading the file."),
                    () => console.log('Completed file download.'));

    let url = window.URL.createObjectURL(thefile);
    window.open(url);
}

Demi kelengkapan, layanan yang mengambil data ada di bawah ini, tetapi satu-satunya yang dilakukannya adalah mengeluarkan permintaan dan meneruskan data tanpa memetakan jika berhasil:

downloadfile(runname: string, type: string){
   return this.authHttp.get( this.files_api + this.title +"/"+ runname + "/?file="+ type)
            .catch(this.logAndPassOn);
}

Anda tidak dapat mengunduh file besar dengan metode ini. Anda akan mencapai batas memori per tab. Ini mungkin serendah 1-2GB.
Matius B.

@ MatthewB. berharap Anda mengatakan apa yang lebih baik.
steve

Untuk unduhan file besar, Anda perlu menentukan tab baru misalnya jika mensimulasikan klik <A>, target harus sama dengan "_blank" Atau lakukan pengiriman formulir. Saya tidak berpikir ada cara bersih untuk mengatasi batasan ukuran file besar dengan permintaan gaya Ajax.
Matius B.

Jawaban:


180

Masalahnya adalah bahwa observable berjalan dalam konteks lain, jadi ketika Anda mencoba membuat URL var, Anda memiliki objek kosong dan bukan gumpalan yang Anda inginkan.

Salah satu dari banyak cara yang ada untuk menyelesaikan ini adalah sebagai berikut:

this._reportService.getReport().subscribe(data => this.downloadFile(data)),//console.log(data),
                 error => console.log('Error downloading the file.'),
                 () => console.info('OK');

Ketika permintaan sudah siap, ia akan memanggil fungsi "downloadFile" yang didefinisikan sebagai berikut:

downloadFile(data: Response) {
  const blob = new Blob([data], { type: 'text/csv' });
  const url= window.URL.createObjectURL(blob);
  window.open(url);
}

gumpalan telah dibuat dengan sempurna dan demikian juga URL var, jika tidak membuka jendela baru, periksa apakah Anda sudah mengimpor 'rxjs / Rx';

  import 'rxjs/Rx' ;

Saya harap ini dapat membantu Anda.


9
Apa itu this._reportService.getReport()dan apa kembalinya?
Burjua

3
@ Burjua the getReport()return athis.http.get(PriceConf.download.url)
ji-ruh

6
Masalah yang saya alami adalah bahwa jendela terbuka dan ditutup segera tidak mengunduh file
Braden Brown

7
Bagaimana kita mengatur nama file di sini? secara default ia memilih nilai numerik sebagai nama
Saurabh

8
Saya telah menggunakan kode di atas untuk mengunduh file dari respons API tetapi saya mendapatkan beberapa kesalahan dalam membuat bagian Blob "Ketik respons tidak dapat ditugaskan untuk mengetik Blobpart". Mohon bantu jika ada yang tahu masalah ini
knbibin

92

Coba ini !

1 - Instal dependensi untuk pop-up file save / buka file

npm install file-saver --save
npm install @types/file-saver --save

2- Buat layanan dengan fungsi ini untuk menerima data

downloadFile(id): Observable<Blob> {
    let options = new RequestOptions({responseType: ResponseContentType.Blob });
    return this.http.get(this._baseUrl + '/' + id, options)
        .map(res => res.blob())
        .catch(this.handleError)
}

3- Pada komponen parsing gumpalan dengan 'file-saver'

import {saveAs as importedSaveAs} from "file-saver";

  this.myService.downloadFile(this.id).subscribe(blob => {
            importedSaveAs(blob, this.fileName);
        }
    )

Ini bekerja untuk saya!


1
Saya menggunakan langkah 2 dalam kombinasi dengan jawaban dari @Alejandro dan itu berhasil tanpa perlu menginstal file-saver ...
Ewert

5
Terima kasih! Itu bekerja dengan sempurna! Saya bertanya-tanya apakah kita bisa mendapatkan nama file yang didefinisikan pada header respons. Apakah itu mungkin?
jfajunior

2
error Av5 Argumen tipe 'RequestOptions' tidak dapat dialihkan ke parameter tipe '{header ?: HttpHeaders | {[header: string]: string | tali[]; };
beri pekerjaan

Namun yang ini tidak cocok untuk mengunduh file besar.
Reven

61

Jika Anda tidak perlu menambahkan header dalam permintaan, untuk mengunduh file di Angular2 Anda dapat melakukan yang sederhana :

window.location.href='http://example.com/myuri/report?param=x';

dalam komponen Anda.


4
Adakah yang bisa memberi tahu mengapa jawaban ini dibatalkan? Topiknya adalah mengunduh file menggunakan angular2. Jika metode ini berfungsi untuk melakukan unduhan sederhana, maka itu juga harus ditandai sebagai jawaban yang valid.
Saurabh Shetty

5
@SaurabhShetty, Ini tidak akan membantu jika Anda ingin mengirim tajuk khusus, bagaimana jika Anda ingin mengirim token auth misalnya? Jika Anda melihat ke pertanyaan OP Anda dapat melihat dia menggunakan authHttp!
A.Akram

6
Saya mengerti downvotes, namun jawaban ini menyelesaikan masalah saya.
JoeriShoeby

1
Jika Anda membiarkan server mengembalikan url dalam beberapa konteks, server dapat menyiapkan url. mis: objek: MyRecord.Cover. Penutup bisa berupa url ke gambar di server. Saat memanggil get (Myrecord) Anda membiarkan server mengembalikan url (Cover) yang disiapkan, dengan token keamanan dan set header lainnya.
Jens Alenius

2
Itu adalah jawaban yang berhasil. Hanya karena itu tidak memiliki <masukkan fitur berguna di sini> yang membuatnya tidak menjadi jawaban.
gburton

46

Ini untuk orang yang mencari cara melakukannya menggunakan HttpClient dan file-saver:

  1. Instal penghemat file

npm instal file-saver - simpan

npm instal @ types / file-saver --save

Kelas Layanan API:

export() {
    return this.http.get(this.download_endpoint, 
        {responseType: 'blob'});
}

Komponen:

import { saveAs } from 'file-saver';
exportPdf() {
    this.api_service.export().subscribe(data => saveAs(data, `pdf report.pdf`));
}

1
Bagaimana cara menunjukkan ukuran file di browser saat unduhan dimulai? Saya mengirim filesize sebagai panjang konten di header http.
humbleCoder

35

Bagaimana dengan ini?

this.http.get(targetUrl,{responseType:ResponseContentType.Blob})
        .catch((err)=>{return [do yourself]})
        .subscribe((res:Response)=>{
          var a = document.createElement("a");
          a.href = URL.createObjectURL(res.blob());
          a.download = fileName;
          // start download
          a.click();
        })

Saya bisa melakukannya.
tidak perlu paket tambahan.


3
Begitu sederhana namun itu adalah yang bekerja dengan sempurna. Itu tidak mengacaukan DOM, tidak membuat elemen apa pun. Saya mengkombinasikan solusi ini dengan beberapa hal di atas dan ini berfungsi seperti pesona.
Chax

20

Seperti yang disebutkan oleh Alejandro Corredor itu adalah kesalahan ruang lingkup yang sederhana. The subscribedijalankan asynchronously danopen harus ditempatkan dalam konteks itu, sehingga data selesai memuat ketika kita memicu unduhan.

Yang mengatakan, ada dua cara untuk melakukannya. Seperti yang direkomendasikan oleh dokumen, layanan menangani pengambilan dan pemetaan data:

//On the service:
downloadfile(runname: string, type: string){
  var headers = new Headers();
  headers.append('responseType', 'arraybuffer');
  return this.authHttp.get( this.files_api + this.title +"/"+ runname + "/?file="+ type)
            .map(res => new Blob([res],{ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }))
            .catch(this.logAndPassOn);
}

Kemudian, pada komponen kita hanya berlangganan dan berurusan dengan data yang dipetakan. Ada dua kemungkinan. Yang pertama , seperti yang disarankan dalam posting asli, tetapi perlu koreksi kecil seperti dicatat oleh Alejandro:

//On the component
downloadfile(type: string){
  this.pservice.downloadfile(this.rundata.name, type)
      .subscribe(data => window.open(window.URL.createObjectURL(data)),
                  error => console.log("Error downloading the file."),
                  () => console.log('Completed file download.'));
  }

Cara kedua adalah menggunakan FileReader. Logikanya sama tetapi kita bisa secara eksplisit menunggu FileReader memuat data, menghindari sarang, dan menyelesaikan masalah async.

//On the component using FileReader
downloadfile(type: string){
    var reader = new FileReader();
    this.pservice.downloadfile(this.rundata.name, type)
        .subscribe(res => reader.readAsDataURL(res), 
                    error => console.log("Error downloading the file."),
                    () => console.log('Completed file download.'));

    reader.onloadend = function (e) {
        window.open(reader.result, 'Excel', 'width=20,height=10,toolbar=0,menubar=0,scrollbars=no');
  }
}

Catatan: Saya mencoba mengunduh file Excel, dan meskipun unduhannya dipicu (jadi ini menjawab pertanyaan), file tersebut rusak. Lihat jawaban untuk posting ini untuk menghindari file yang rusak.


7
Saya pikir alasan file menjadi rusak adalah karena Anda memuat reske dalam gumpalan dan Anda benar-benar ingin res._body. Namun _bodyvariabel pribadi dan tidak dapat diakses. Pada hari ini .blob()dan .arrayBuffer()pada objek respon http belum diimplementasikan dalam Angular 2. text()dan json()merupakan satu-satunya dua pilihan tetapi keduanya akan memutarbalikkan tubuh Anda. Sudahkah Anda menemukan solusinya?
sschueller

1
hai @ rll, saya mengikuti langkah-langkah di atas dan saya mendapatkan berlangganan setelah selesai. Namun saya tidak dapat melihat file tersebut diunduh. Saya tidak bisa melihat kesalahan juga. Tolong bantu
AishApp

1
Opsi 2 memungkinkan saya mengunduh file, tetapi pertama-tama memuat data di latar belakang. Bagaimana jika saya memiliki file besar yang harus diunduh?
f123

1
Solusi saya adalah cukup gunakan <a href=""></a>untuk mengunduh file.
user2061057

1
Saya tahu ini adalah jawaban lama tetapi sangat tinggi pada hasil pencarian dan merupakan jawaban yang diterima: Baris `headers.append ('responseType', 'arraybuffer');` salah. Itu pilihan, bukan tajuk. Tolong perbaiki. Aaaand ... Header dibuat dan tidak digunakan. Tidak membantu.
Stevo

17

Unduh * .zip solution untuk angular 2.4.x: Anda harus mengimpor ResponseContentType dari '@ angular / http' dan mengubah responseType ke ResponseContentType.ArrayBuffer (secara default, ResponseContentType.Json)

getZip(path: string, params: URLSearchParams = new URLSearchParams()): Observable<any> {
 let headers = this.setHeaders({
      'Content-Type': 'application/zip',
      'Accept': 'application/zip'
    });

 return this.http.get(`${environment.apiUrl}${path}`, { 
   headers: headers, 
   search: params, 
   responseType: ResponseContentType.ArrayBuffer //magic
 })
          .catch(this.formatErrors)
          .map((res:Response) => res['_body']);
}

16

Untuk versi sudut yang lebih baru:

npm install file-saver --save
npm install @types/file-saver --save


import {saveAs} from 'file-saver/FileSaver';

this.http.get('endpoint/', {responseType: "blob", headers: {'Accept': 'application/pdf'}})
  .subscribe(blob => {
    saveAs(blob, 'download.pdf');
  });

Terima kasih, bekerja dengan Angular 8. Tidak tahu mengapa ini sangat sulit ditemukan.
MDave

11

Mengunduh file melalui ajax selalu merupakan proses yang menyakitkan dan Dalam pandangan saya yang terbaik adalah membiarkan server dan browser melakukan pekerjaan negosiasi tipe konten ini.

Saya pikir yang terbaik untuk dimiliki

<a href="api/sample/download"></a> 

untuk melakukannya. Ini bahkan tidak memerlukan pembukaan jendela baru dan hal-hal seperti itu.

Pengontrol MVC seperti pada sampel Anda dapat seperti di bawah ini:

[HttpGet("[action]")]
public async Task<FileContentResult> DownloadFile()
{
    // ...
    return File(dataStream.ToArray(), "text/plain", "myblob.txt");
}

1
Anda benar, tetapi kemudian bagaimana Anda bisa mengelola kesalahan server dalam aplikasi satu halaman? Jika terjadi kesalahan, biasanya, layanan REST mengembalikan JSON dengan kesalahan, sehingga mengakibatkan aplikasi untuk membuka JSON di jendela browser lain, yang bukan apa yang ingin dilihat pengguna
Luca

2
Jika Anda memiliki token akses, Anda harus menyediakan ini tidak berfungsi
chris31389

Ini sederhana dan sederhana. Tetapi jika Anda ingin melakukan otentikasi, maka ada kemungkinan memiliki sesuatu seperti token satu kali. Jadi, alih-alih seperti ini, Anda dapat memiliki url sebagai: example.com/myuri/report?tokenid=1234-1233 Dan verifikasi token ID dalam database. Tentu saja ini bukan skenario sederhana dan berfungsi dalam semua situasi, tetapi dapat menjadi solusi dalam situasi di mana, Anda memiliki akses ke database sebelum mengembalikan laporan sebagai aliran ..
GingerBeer

Dapatkan url unduhan dari server. Jadi server dapat menyiapkan url dengan token keamanan satu kali.
Jens Alenius

8

Saya menggunakan Angular 4 dengan objek 4.3 httpClient. Saya mengubah jawaban yang saya temukan di Blog Teknis Js 'yang membuat objek tautan, menggunakannya untuk melakukan pengunduhan, kemudian menghancurkannya.

Klien:

doDownload(id: number, contentType: string) {
    return this.http
        .get(this.downloadUrl + id.toString(), { headers: new HttpHeaders().append('Content-Type', contentType), responseType: 'blob', observe: 'body' })
}

downloadFile(id: number, contentType: string, filename:string)  {

    return this.doDownload(id, contentType).subscribe(  
        res => { 
            var url = window.URL.createObjectURL(res);
            var a = document.createElement('a');
            document.body.appendChild(a);
            a.setAttribute('style', 'display: none');
            a.href = url;
            a.download = filename;
            a.click();
            window.URL.revokeObjectURL(url);
            a.remove(); // remove the element
        }, error => {
            console.log('download error:', JSON.stringify(error));
        }, () => {
            console.log('Completed file download.')
        }); 

} 

Nilai this.downloadUrl telah ditetapkan sebelumnya untuk menunjuk ke api. Saya menggunakan ini untuk mengunduh lampiran, jadi saya tahu id, contentType dan nama file: Saya menggunakan api MVC untuk mengembalikan file:

 [ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
    public FileContentResult GetAttachment(Int32 attachmentID)
    { 
        Attachment AT = filerep.GetAttachment(attachmentID);            
        if (AT != null)
        {
            return new FileContentResult(AT.FileBytes, AT.ContentType);  
        }
        else
        { 
            return null;
        } 
    } 

Kelas lampiran terlihat seperti ini:

 public class Attachment
{  
    public Int32 AttachmentID { get; set; }
    public string FileName { get; set; }
    public byte[] FileBytes { get; set; }
    public string ContentType { get; set; } 
}

Repositori filerep mengembalikan file dari database.

Semoga ini bisa membantu seseorang :)


7

Bagi mereka yang menggunakan Pola Redux

Saya menambahkan dalam file-saver sebagai @Hector Cuevas disebutkan dalam jawabannya. Menggunakan Angular2 v. 2.3.1, saya tidak perlu menambahkan @ types / file-saver.

Contoh berikut adalah mengunduh jurnal sebagai PDF.

Tindakan jurnal

public static DOWNLOAD_JOURNALS = '[Journals] Download as PDF';
public downloadJournals(referenceId: string): Action {
 return {
   type: JournalActions.DOWNLOAD_JOURNALS,
   payload: { referenceId: referenceId }
 };
}

public static DOWNLOAD_JOURNALS_SUCCESS = '[Journals] Download as PDF Success';
public downloadJournalsSuccess(blob: Blob): Action {
 return {
   type: JournalActions.DOWNLOAD_JOURNALS_SUCCESS,
   payload: { blob: blob }
 };
}

Efek jurnal

@Effect() download$ = this.actions$
    .ofType(JournalActions.DOWNLOAD_JOURNALS)
    .switchMap(({payload}) =>
        this._journalApiService.downloadJournal(payload.referenceId)
        .map((blob) => this._actions.downloadJournalsSuccess(blob))
        .catch((err) => handleError(err, this._actions.downloadJournalsFail(err)))
    );

@Effect() downloadJournalSuccess$ = this.actions$
    .ofType(JournalActions.DOWNLOAD_JOURNALS_SUCCESS)
    .map(({payload}) => saveBlobAs(payload.blob, 'journal.pdf'))

Layanan jurnal

public downloadJournal(referenceId: string): Observable<any> {
    const url = `${this._config.momentumApi}/api/journals/${referenceId}/download`;
    return this._http.getBlob(url);
}

Layanan HTTP

public getBlob = (url: string): Observable<any> => {
    return this.request({
        method: RequestMethod.Get,
        url: url,
        responseType: ResponseContentType.Blob
    });
};

Peredam jurnal Meskipun ini hanya menetapkan status yang benar yang digunakan dalam aplikasi kita, saya masih ingin menambahkannya untuk menunjukkan pola yang lengkap.

case JournalActions.DOWNLOAD_JOURNALS: {
  return Object.assign({}, state, <IJournalState>{ downloading: true, hasValidationErrors: false, errors: [] });
}

case JournalActions.DOWNLOAD_JOURNALS_SUCCESS: {
  return Object.assign({}, state, <IJournalState>{ downloading: false, hasValidationErrors: false, errors: [] });
}

Saya harap ini bermanfaat.


7

Saya membagikan solusi yang membantu saya (setiap perbaikan sangat dihargai)

Pada layanan Anda ' layanan ':

getMyFileFromBackend(typeName: string): Observable<any>{
    let param = new URLSearchParams();
    param.set('type', typeName);
    // setting 'responseType: 2' tells angular that you are loading an arraybuffer
    return this.http.get(http://MYSITE/API/FILEIMPORT, {search: params, responseType: 2})
            .map(res => res.text())
            .catch((error:any) => Observable.throw(error || 'Server error'));
}

Bagian komponen :

downloadfile(type: string){
   this.pservice.getMyFileFromBackend(typename).subscribe(
                    res => this.extractData(res),
                    (error:any) => Observable.throw(error || 'Server error')
                );
}

extractData(res: string){
    // transforme response to blob
    let myBlob: Blob = new Blob([res], {type: 'application/vnd.oasis.opendocument.spreadsheet'}); // replace the type by whatever type is your response

    var fileURL = URL.createObjectURL(myBlob);
    // Cross your fingers at this point and pray whatever you're used to pray
    window.open(fileURL);
}

Pada bagian komponen, Anda memanggil layanan tanpa berlangganan tanggapan. Berlangganan untuk daftar lengkap tipe mime openOffice lihat: http://www.openoffice.org/framework/documentation/mimetypes/mimetypes.html


7

Akan lebih baik jika Anda mencoba memanggil metode baru di dalam diri Anda subscribe

this._reportService.getReport()
    .subscribe((data: any) => {
        this.downloadFile(data);
    },
        (error: any) => сonsole.log(error),
        () => console.log('Complete')
    );

downloadFile(data)Fungsi dalam kita perlu buatblock, link, href and file name

downloadFile(data: any, type: number, name: string) {
    const blob = new Blob([data], {type: 'text/csv'});
    const dataURL = window.URL.createObjectURL(blob);

    // IE doesn't allow using a blob object directly as link href
    // instead it is necessary to use msSaveOrOpenBlob
    if (window.navigator && window.navigator.msSaveOrOpenBlob) {
      window.navigator.msSaveOrOpenBlob(blob);
      return;
    }

    const link = document.createElement('a');
    link.href = dataURL;
    link.download = 'export file.csv';
    link.click();

    setTimeout(() => {

      // For Firefox it is necessary to delay revoking the ObjectURL
      window.URL.revokeObjectURL(dataURL);
      }, 100);
    }
}

5

Untuk mengunduh dan menampilkan file PDF , kode yang sangat mirip dipotong seperti di bawah ini:

  private downloadFile(data: Response): void {
    let blob = new Blob([data.blob()], { type: "application/pdf" });
    let url = window.URL.createObjectURL(blob);
    window.open(url);
  }

  public showFile(fileEndpointPath: string): void {
    let reqOpt: RequestOptions = this.getAcmOptions();  //  getAcmOptions is our helper method. Change this line according to request headers you need.
    reqOpt.responseType = ResponseContentType.Blob;
    this.http
      .get(fileEndpointPath, reqOpt)
      .subscribe(
        data => this.downloadFile(data),
        error => alert("Error downloading file!"),
        () => console.log("OK!")
      );
  }

5

Inilah sesuatu yang saya lakukan dalam kasus saya -

// service method
downloadFiles(vendorName, fileName) {
    return this.http.get(this.appconstants.filesDownloadUrl, { params: { vendorName: vendorName, fileName: fileName }, responseType: 'arraybuffer' }).map((res: ArrayBuffer) => { return res; })
        .catch((error: any) => _throw('Server error: ' + error));
}

// a controller function which actually downloads the file
saveData(data, fileName) {
    var a = document.createElement("a");
    document.body.appendChild(a);
    a.style = "display: none";
    let blob = new Blob([data], { type: "octet/stream" }),
        url = window.URL.createObjectURL(blob);
    a.href = url;
    a.download = fileName;
    a.click();
    window.URL.revokeObjectURL(url);
}

// a controller function to be called on requesting a download
downloadFiles() {
    this.service.downloadFiles(this.vendorName, this.fileName).subscribe(data => this.saveData(data, this.fileName), error => console.log("Error downloading the file."),
        () => console.info("OK"));
}

Solusinya dirujuk dari - sini


4

Perbarui ke jawaban Hector menggunakan file-saver dan HttpClient untuk langkah 2:

public downloadFile(file: File): Observable<Blob> {
    return this.http.get(file.fullPath, {responseType: 'blob'})
}

3

Saya mendapat solusi untuk mengunduh dari angular 2 tanpa menjadi korup, menggunakan spring mvc dan angular 2

1st - tipe pengembalian saya adalah: - ResponseEntity dari java end. Di sini saya mengirim array byte [] memiliki tipe kembali dari controller.

2 - untuk memasukkan fileaver di ruang kerja Anda-di halaman indeks sebagai:

<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2014-11-29/FileSaver.min.js"></script>

3rd- at ts komponen menulis kode ini:

import {ResponseContentType} from '@angular.core';

let headers = new Headers({ 'Content-Type': 'application/json', 'MyApp-Application' : 'AppName', 'Accept': 'application/pdf' });
        let options = new RequestOptions({ headers: headers, responseType: ResponseContentType.Blob });
            this.http
            .post('/project/test/export',
                    somevalue,options)
              .subscribe(data => {

                  var mediaType = 'application/vnd.ms-excel';
                  let blob: Blob = data.blob();
                    window['saveAs'](blob, 'sample.xls');

                });

Ini akan memberi Anda format file xls. Jika Anda ingin format lain, ubah mediatype dan nama file dengan ekstensi yang benar.


3

Saya menghadapi kasus yang sama hari ini, saya harus mengunduh file pdf sebagai lampiran (file tidak boleh di-render di browser, tetapi diunduh sebagai gantinya). Untuk mencapai itu saya menemukan saya harus mendapatkan file dalam Angular Blob, dan, pada saat yang sama, tambahkan aContent-Disposition header di respons.

Ini adalah yang paling sederhana yang bisa saya dapatkan (Angular 7):

Di dalam layanan:

getFile(id: String): Observable<HttpResponse<Blob>> {
  return this.http.get(`./file/${id}`, {responseType: 'blob', observe: 'response'});
}

Kemudian, ketika saya perlu mengunduh file dalam suatu komponen, saya dapat dengan mudah:

fileService.getFile('123').subscribe((file: HttpResponse<Blob>) => window.location.href = file.url);

MEMPERBARUI:

Menghapus pengaturan tajuk yang tidak perlu dari layanan


Jika saya menggunakan window.location.href, bukan window.open, Chrome memperlakukannya sebagai unduhan banyak file.
DanO

ini tidak akan berfungsi jika Anda memiliki token
autentikasi

3

Kode berikut ini berfungsi untuk saya

let link = document.createElement('a');
link.href = data.fileurl; //data is object received as response
link.download = data.fileurl.substr(data.fileurl.lastIndexOf('/') + 1);
link.click();

2

Saya menemukan jawabannya sejauh ini kurang wawasan dan juga peringatan. Anda dapat dan harus memperhatikan ketidakcocokan dengan IE10 + (jika Anda peduli).

Ini adalah contoh lengkap dengan bagian aplikasi dan bagian layanan setelahnya. Perhatikan bahwa kita mengatur observ: "response" untuk menangkap header untuk nama file. Perhatikan juga bahwa header Content-Disposition harus diatur dan diekspos oleh server, jika tidak, HttpClient Angular saat ini tidak akan meneruskannya. Saya menambahkan potongan kode inti dotnet untuk itu di bawah ini.

public exportAsExcelFile(dataId: InputData) {
    return this.http.get(this.apiUrl + `event/export/${event.id}`, {
        responseType: "blob",
        observe: "response"
    }).pipe(
        tap(response => {
            this.downloadFile(response.body, this.parseFilename(response.headers.get('Content-Disposition')));
        })
    );
}

private downloadFile(data: Blob, filename: string) {
    const blob = new Blob([data], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8;'});
    if (navigator.msSaveBlob) { // IE 10+
        navigator.msSaveBlob(blob, filename);
    } else {
        const link = document.createElement('a');
        if (link.download !== undefined) {
            // Browsers that support HTML5 download attribute
            const url = URL.createObjectURL(blob);
            link.setAttribute('href', url);
            link.setAttribute('download', filename);
            link.style.visibility = 'hidden';
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }
    }
}

private parseFilename(contentDisposition): string {
    if (!contentDisposition) return null;
    let matches = /filename="(.*?)"/g.exec(contentDisposition);

    return matches && matches.length > 1 ? matches[1] : null;
}

Inti Dotnet, dengan Content-Disposition & MediaType

 private object ConvertFileResponse(ExcelOutputDto excelOutput)
    {
        if (excelOutput != null)
        {
            ContentDisposition contentDisposition = new ContentDisposition
            {
                FileName = excelOutput.FileName.Contains(_excelExportService.XlsxExtension) ? excelOutput.FileName : "TeamsiteExport.xlsx",
                Inline = false
            };
            Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");
            Response.Headers.Add("Content-Disposition", contentDisposition.ToString());
            return File(excelOutput.ExcelSheet, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        }
        else
        {
            throw new UserFriendlyException("The excel output was empty due to no events.");
        }
    }

1
 let headers = new Headers({
                'Content-Type': 'application/json',
                'MyApp-Application': 'AppName',
                'Accept': 'application/vnd.ms-excel'
            });
            let options = new RequestOptions({
                headers: headers,
                responseType: ResponseContentType.Blob
            });


this.http.post(this.urlName + '/services/exportNewUpc', localStorageValue, options)
                .subscribe(data => {
                    if (navigator.appVersion.toString().indexOf('.NET') > 0)
                    window.navigator.msSaveBlob(data.blob(), "Export_NewUPC-Items_" + this.selectedcategory + "_" + this.retailname +"_Report_"+this.myDate+".xlsx");

                    else {
                        var a = document.createElement("a");
                        a.href = URL.createObjectURL(data.blob());
                        a.download = "Export_NewUPC-Items_" + this.selectedcategory + "_" + this.retailname +"_Report_"+this.myDate+ ".xlsx";
                        a.click();
                    }
                    this.ui_loader = false;
                    this.selectedexport = 0;
                }, error => {
                    console.log(error.json());
                    this.ui_loader = false;
                    document.getElementById("exceptionerror").click();
                });

1

Cukup menempatkan urlsebagai hrefsebagai berikut.

<a href="my_url">Download File</a>

Apakah itu bekerja? Saya mendapatkan kesalahan ... "ERROR TypeError:" Akses ke 'file: ///Downloads/test.json' dari skrip ditolak. ""
Jay

Terima kasih bisa tolong bagikan bagaimana url Anda terlihat? Id it file protokol atau http atau yang lain?
Jay

Ini protokol file.
Harunur Rashid


1

Anda juga dapat mengunduh file langsung dari templat tempat Anda menggunakan atribut unduhan dan [attr.href]Anda dapat memberikan nilai properti dari komponen. Solusi sederhana ini dapat digunakan di sebagian besar browser.

<a download [attr.href]="yourDownloadLink"></a>

Referensi: https://www.w3schools.com/tags/att_a_download.asp


1
Selamat datang di SO! Silakan periksa apakah koreksi (pengaturan huruf dan tata bahasa) saya membantu.
B - rian

0

Jika Anda hanya mengirim parameter ke URL, Anda bisa melakukannya dengan cara ini:

downloadfile(runname: string, type: string): string {
   return window.location.href = `${this.files_api + this.title +"/"+ runname + "/?file="+ type}`;
}

dalam layanan yang menerima parameter


0

IniJawaban menunjukkan bahwa Anda tidak dapat mengunduh file secara langsung dengan AJAX, terutama karena alasan keamanan. Jadi saya akan menjelaskan apa yang saya lakukan dalam situasi ini,

01. Tambahkan hrefatribut di tag jangkar Anda di dalam component.htmlfile,
misalnya: -

<div>
       <a [href]="fileUrl" mat-raised-button (click)='getGenaratedLetterTemplate(element)'> GENARATE </a>
</div>

02. Lakukan semua langkah berikut di dalam Anda component.tsuntuk memintas tingkat keamanan dan bawa dialog save as popup,
mis: -

import { environment } from 'environments/environment';
import { DomSanitizer } from '@angular/platform-browser';
export class ViewHrApprovalComponent implements OnInit {
private apiUrl = environment.apiUrl;
  fileUrl
 constructor(
    private sanitizer: DomSanitizer,
    private letterService: LetterService) {}
getGenaratedLetterTemplate(letter) {

    this.data.getGenaratedLetterTemplate(letter.letterId).subscribe(
      // cannot download files directly with AJAX, primarily for security reasons);
    console.log(this.apiUrl + 'getGeneratedLetter/' + letter.letterId);
    this.fileUrl = this.sanitizer.bypassSecurityTrustResourceUrl(this.apiUrl + 'getGeneratedLetter/' + letter.letterId);
  }

Catatan: Jawaban ini akan berfungsi jika Anda mendapatkan kesalahan "OK" dengan kode status 200


0

Yah, saya menulis sepotong kode yang terinspirasi oleh banyak jawaban di atas yang seharusnya dapat dengan mudah bekerja di sebagian besar skenario di mana server mengirim file dengan header disposisi konten, tanpa instalasi pihak ketiga, kecuali rxjs dan angular.

Pertama, cara memanggil kode dari file komponen Anda

this.httpclient.get(
   `${myBackend}`,
   {
      observe: 'response',
      responseType: 'blob'
   }
).pipe(first())
.subscribe(response => SaveFileResponse(response, 'Custom File Name.extension'));

Seperti yang Anda lihat, itu pada dasarnya cukup banyak panggilan backend rata-rata dari sudut, dengan dua perubahan

  1. Saya mengamati respons alih-alih tubuh
  2. Saya secara eksplisit tentang respon menjadi gumpalan

Setelah file diambil dari server, saya pada prinsipnya, mendelegasikan seluruh tugas menyimpan file ke fungsi helper, yang saya simpan di file terpisah, dan impor ke komponen mana pun yang saya butuhkan.

export const SaveFileResponse = 
(response: HttpResponse<Blob>, 
 filename: string = null) => 
{
    //null-checks, just because :P
    if (response == null || response.body == null)
        return;

    let serverProvidesName: boolean = true;
    if (filename != null)
        serverProvidesName = false;

    //assuming the header is something like
    //content-disposition: attachment; filename=TestDownload.xlsx; filename*=UTF-8''TestDownload.xlsx
    if (serverProvidesName)
        try {
            let f: string = response.headers.get('content-disposition').split(';')[1];
            if (f.includes('filename='))
                filename = f.substring(10);
        }
        catch { }
    SaveFile(response.body, filename);
}

//Create an anchor element, attach file to it, and
//programmatically click it. 
export const SaveFile = (blobfile: Blob, filename: string = null) => {
    const a = document.createElement('a');
    a.href = window.URL.createObjectURL(blobfile);
    a.download = filename;
    a.click();
}

Di sana, tidak ada lagi nama file GUID samar! Kita dapat menggunakan nama apa pun yang disediakan server, tanpa harus menentukannya secara eksplisit di klien, atau, menimpa nama file yang disediakan oleh server (seperti dalam contoh ini). Juga, orang dapat dengan mudah, jika perlu, mengubah algoritma mengekstraksi nama file dari disposisi konten agar sesuai dengan kebutuhan mereka, dan segala sesuatu yang lain akan tetap tidak terpengaruh - jika terjadi kesalahan selama ekstraksi seperti itu, itu hanya akan melewati 'nol' sebagai nama file.

Seperti jawaban lain yang sudah ditunjukkan, IE memerlukan beberapa perlakuan khusus, seperti biasa. Tetapi dengan kromium yang akan datang dalam beberapa bulan, saya tidak akan khawatir tentang hal itu sambil membangun aplikasi baru (mudah-mudahan). Ada juga masalah mencabut URL, tapi saya agak tidak begitu yakin tentang itu, jadi jika seseorang bisa membantu dengan itu di komentar, itu akan luar biasa.


0

Jika sebuah tab membuka dan menutup tanpa mengunduh apa pun, saya mencoba mengikuti dengan tautan mock anchor dan itu berhasil.

downloadFile(x: any) {
var newBlob = new Blob([x], { type: "application/octet-stream" });

    // IE doesn't allow using a blob object directly as link href
    // instead it is necessary to use msSaveOrOpenBlob
    if (window.navigator && window.navigator.msSaveOrOpenBlob) {
      window.navigator.msSaveOrOpenBlob(newBlob);
      return;
    }

    // For other browsers: 
    // Create a link pointing to the ObjectURL containing the blob.
    const data = window.URL.createObjectURL(newBlob);

    var link = document.createElement('a');
    link.href = data;
    link.download = "mapped.xlsx";
    // this is necessary as link.click() does not work on the latest firefox
    link.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window }));

    setTimeout(function () {
      // For Firefox it is necessary to delay revoking the ObjectURL
      window.URL.revokeObjectURL(data);
      link.remove();
    }, 100);  }
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.