Làm cách nào để tải xuống tệp có Angular2 trở lên


181

Tôi có một ứng dụng WebApi / MVC mà tôi đang phát triển ứng dụng khách angular2 (để thay thế MVC). Tôi gặp một số rắc rối khi hiểu cách Angular lưu tệp.

Yêu cầu vẫn ổn (hoạt động tốt với MVC và chúng tôi có thể đăng nhập dữ liệu nhận được) nhưng tôi không thể tìm ra cách lưu dữ liệu đã tải xuống (tôi chủ yếu tuân theo logic tương tự như trong bài đăng này ). Tôi chắc chắn rằng nó đơn giản một cách ngu ngốc, nhưng cho đến nay tôi chỉ đơn giản là không nắm bắt được nó.

Mã của hàm thành phần dưới đây. Tôi đã thử các lựa chọn khác nhau, cách blob nên là cách để đi xa như tôi hiểu, nhưng không có chức năng createObjectURLtrong URL. Tôi thậm chí không thể tìm thấy định nghĩa URLtrong cửa sổ, nhưng rõ ràng nó tồn tại. Nếu tôi sử dụng FileSaver.jsmô-đun, tôi nhận được cùng một lỗi. Vì vậy, tôi đoán đây là một cái gì đó đã thay đổi gần đây hoặc chưa được thực hiện. Làm cách nào để kích hoạt tệp lưu trong 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);
}

Để hoàn thiện, dịch vụ tìm nạp dữ liệu ở bên dưới, nhưng điều duy nhất nó làm là đưa ra yêu cầu và truyền dữ liệu mà không cần ánh xạ nếu thành công:

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

Bạn không thể tải xuống các tệp lớn với phương pháp này. Bạn sẽ đạt giới hạn bộ nhớ cho mỗi tab. Điều này có thể thấp đến 1-2GB.
Matthew B.

@MatthewB. ước gì bạn đã nói những gì tốt hơn
steve

Để tải xuống tệp lớn, bạn cần chỉ định một tab mới, ví dụ: nếu mô phỏng nhấp chuột <A>, mục tiêu cần bằng "_blank" Hoặc gửi biểu mẫu. Tôi không nghĩ có một cách rõ ràng để vượt qua giới hạn kích thước tệp lớn với các yêu cầu kiểu Ajax.
Matthew B.

Câu trả lời:


180

Vấn đề là có thể quan sát được chạy trong bối cảnh khác, vì vậy khi bạn cố gắng tạo URL var, bạn có một đối tượng trống và không phải là blob bạn muốn.

Một trong nhiều cách tồn tại để giải quyết vấn đề này như sau:

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

Khi yêu cầu đã sẵn sàng, nó sẽ gọi hàm "downloadFile" được định nghĩa như sau:

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

blob đã được tạo hoàn hảo và vì vậy URL var, nếu không mở cửa sổ mới, vui lòng kiểm tra xem bạn đã nhập 'rxjs / Rx' chưa;

  import 'rxjs/Rx' ;

Tôi hy vọng điều này có thể giúp bạn.


9
Nó là this._reportService.getReport()gì và nó trở lại là gì?
Burjua

3
@Burjua getReport()trả lại athis.http.get(PriceConf.download.url)
ji-ruh

6
Vấn đề tôi gặp phải là cửa sổ mở và đóng ngay lập tức không tải xuống tệp
Braden Brown

7
Làm thế nào chúng ta có thể đặt tên tập tin ở đây? theo mặc định, nó chọn một giá trị số làm tên
Saurabh

8
Tôi đã sử dụng mã ở trên để tải xuống tệp từ phản hồi API nhưng tôi gặp một số lỗi khi tạo phần Blob "Loại phản hồi không thể gán cho loại Blobpart". Vui lòng giúp đỡ nếu có ai biết vấn đề này
knbibin

91

Hãy thử điều này !

1 - Cài đặt phụ thuộc để hiển thị lưu / mở tệp bật lên

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

2- Tạo một dịch vụ với chức năng này để truy xuất dữ liệu

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- Trong thành phần phân tích cú pháp blob với 'trình tiết kiệm tập tin'

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

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

Điều này làm việc cho tôi!


1
Tôi đã sử dụng bước 2 kết hợp với câu trả lời từ @Alejandro và nó đã hoạt động mà không cần cài đặt trình tiết kiệm tệp ...
Ewert

5
Cảm ơn bạn! Nó hoạt động hoàn hảo! Tôi tự hỏi nếu chúng ta có thể lấy tên tệp được xác định trên tiêu đề của phản hồi. Điều đó có thể không?
jfajunior

2
lỗi Av5 Đối số của loại 'RequestOptions' không thể gán cho tham số của loại '{tiêu đề?: HttpHeaders | {[tiêu đề: chuỗi]: chuỗi | chuỗi[]; };
choJob

Điều này tuy nhiên không phù hợp để tải tập tin lớn.
Reven

60

Nếu bạn không cần thêm tiêu đề trong yêu cầu, để tải xuống tệp trong Angular2, bạn có thể thực hiện một cách đơn giản :

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

trong thành phần của bạn.


4
Ai đó có thể vui lòng cho biết tại sao câu trả lời này bị hạ cấp? Chủ đề là để tải xuống một tập tin bằng angular2. Nếu phương pháp này hoạt động để thực hiện tải xuống đơn giản thì nó cũng nên được đánh dấu là một câu trả lời hợp lệ.
Saurabh Shetty

5
@SaurabhShetty, Điều này sẽ không giúp ích trong trường hợp bạn muốn gửi tiêu đề tùy chỉnh, nếu bạn muốn gửi mã thông báo xác thực chẳng hạn thì sao? Nếu bạn nhìn vào câu hỏi OP bạn có thể thấy anh ấy sử dụng authHttp!
A.Akram

6
Tôi hiểu các downvote, tuy nhiên câu trả lời này đã giải quyết vấn đề của tôi.
JoeriShoither

1
Nếu bạn để máy chủ trả lại url trong một số ngữ cảnh, máy chủ có thể chuẩn bị url. ví dụ: đối tượng: MyRecord.Cover. Bìa có thể là một url đến một hình ảnh trong máy chủ. Khi gọi get (Myrecord), bạn cho phép máy chủ trả lại url đã chuẩn bị (Bìa), với mã thông báo bảo mật và các tiêu đề khác được đặt.
Jens Alenius

2
Đó là một câu trả lời hoạt động. Chỉ cần nó không có <chèn tính năng hữu ích ở đây> mà không làm cho nó không phải là một câu trả lời.
gburton

46

Điều này dành cho những người đang tìm cách làm điều đó bằng cách sử dụng HTTPClient và trình tiết kiệm tệp:

  1. Cài đặt trình tiết kiệm tập tin

npm cài đặt trình tiết kiệm tập tin - lưu

npm install @ type / file-saver --save

Lớp dịch vụ API:

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

Thành phần:

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

1
Làm cách nào để hiển thị kích thước tệp trong trình duyệt khi quá trình tải xuống bắt đầu? Tôi đang gửi kích thước tệp dưới dạng độ dài nội dung trong tiêu đề http.
humbleCoder

35

Còn cái này thì sao?

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

Tôi có thể làm với nó.
Không cần gói bổ sung.


3
Vì vậy, đơn giản nhưng nó là một trong những hoạt động hoàn hảo. Nó không làm lộn xộn DOM, không tạo ra bất kỳ yếu tố nào. Tôi đã kết hợp giải pháp này với một số aboves và nó hoạt động như một bùa mê.
Chax

20

Như Alejandro Corredor đã đề cập, đây là một lỗi phạm vi đơn giản. Nó subscribeđược chạy không đồng bộ vàopen phải được đặt trong bối cảnh đó, để dữ liệu tải xong khi chúng tôi kích hoạt tải xuống.

Điều đó nói rằng, có hai cách để làm điều đó. Vì các tài liệu khuyến nghị nên dịch vụ sẽ đảm nhận việc lấy và ánh xạ dữ liệu:

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

Sau đó, trên thành phần chúng tôi chỉ cần đăng ký và xử lý dữ liệu được ánh xạ. Có hai khả năng. Đầu tiên , như được đề xuất trong bài viết gốc, nhưng cần một sự điều chỉnh nhỏ như Alejandro đã lưu ý:

//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.'));
  }

Cách thứ hai sẽ là sử dụng FileReader. Logic là như nhau nhưng chúng ta có thể chờ đợi FileReader tải dữ liệu một cách rõ ràng, tránh việc lồng nhau và giải quyết vấn đề không đồng bộ.

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

Lưu ý: Tôi đang cố tải xuống một tệp Excel và mặc dù tải xuống được kích hoạt (vì vậy điều này trả lời câu hỏi), nhưng tệp bị hỏng. Xem câu trả lời cho bài đăng này để tránh các tập tin bị hỏng.


7
Tôi nghĩ lý do tập tin bị hỏng là vì bạn đang tải resvào blob và bạn thực sự muốn res._body. Tuy nhiên _bodylà một biến riêng tư và không thể truy cập. Cho đến ngày hôm nay .blob().arrayBuffer()trên một đối tượng phản hồi http đã không được thực hiện trong Angular 2. text()json()là hai tùy chọn duy nhất nhưng cả hai sẽ cắt xén cơ thể bạn. Bạn đã tìm thấy một giải pháp?
sschueller 18/03/2016

1
chào @rll, tôi đã làm theo các bước trên và tôi đang đăng ký khi hoàn thành. Tôi vẫn không thể nhìn thấy các tập tin được tải về. Tôi không thể thấy bất kỳ lỗi nào là tốt. Xin hãy giúp đỡ
AishApp

1
2 tùy chọn cho phép tôi tải xuống tệp, nhưng nó sẽ tải dữ liệu trong nền trước. Nếu tôi có một tệp lớn phải tải xuống thì sao?
f123

1
Giải pháp của tôi là chỉ sử dụng <a href=""></a>để tải về một tập tin.
2061057

1
Tôi biết đây là một câu trả lời cũ nhưng nó rất cao về kết quả tìm kiếm và là câu trả lời được chấp nhận: Dòng `headers.append ('answerType', 'Arraybuffer');` sai. Đó là một lựa chọn, không phải là một tiêu đề. Hãy sửa chữa nó. Aaaand ... Các tiêu đề được tạo và không được sử dụng. Không hữu dụng.
Stevo

17

Tải xuống giải pháp * .zip cho angular 2.4.x: bạn phải nhập FeedbackContentType từ '@ angular / http' và thay đổi answerType thành FeedbackContentType.ArrayBuffer (theo mặc định là FeedbackContentType.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

Đối với các phiên bản góc mới hơn:

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

Cảm ơn, làm việc với Angular 8. Không biết tại sao điều này rất khó tìm.
MDave

11

Tải tập tin qua ajax luôn là một quá trình khó khăn và theo quan điểm của tôi, tốt nhất là để máy chủ và trình duyệt thực hiện công việc đàm phán kiểu nội dung này.

Tôi nghĩ rằng tốt nhất để có

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

để làm điều đó. Điều này thậm chí không yêu cầu bất kỳ cửa sổ mới mở và những thứ như vậy.

Bộ điều khiển MVC như trong mẫu của bạn có thể giống như dưới đây:

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

1
Bạn đúng, nhưng làm thế nào bạn có thể quản lý lỗi máy chủ trong ứng dụng một trang? Trong trường hợp có lỗi, thông thường, dịch vụ REST trả về lỗi JSON, do đó, ứng dụng sẽ mở JSON trong cửa sổ trình duyệt khác, đây không phải là điều người dùng muốn thấy
Luca

2
Nếu bạn có mã thông báo truy cập, bạn cần cung cấp mã thông báo không hoạt động
chris31389

Điều này là đơn giản và đơn giản. Nhưng nếu bạn muốn thực hiện một số xác thực, thì có khả năng có một cái gì đó giống như mã thông báo một lần. Vì vậy, thay vì có nó như thế này, bạn có thể có url như: example.com/myuri/report?tokenid=1234-1233 Và xác minh ID mã thông báo trong cơ sở dữ liệu. Tất nhiên, đây không phải là một kịch bản đơn giản và hoạt động trong tất cả các tình huống, nhưng có thể là một giải pháp trong tình huống, bạn có quyền truy cập vào cơ sở dữ liệu trước khi trả lại báo cáo dưới dạng luồng ..
GingerBeer

Lấy url tải xuống từ máy chủ. Vì vậy, máy chủ có thể chuẩn bị url với mã thông báo bảo mật một lần.
Jens Alenius

8

Tôi đang sử dụng Angular 4 với đối tượng 4.3 httpClient. Tôi đã sửa đổi một câu trả lời tôi tìm thấy trong Blog kỹ thuật của Js để tạo một đối tượng liên kết, sử dụng nó để thực hiện tải xuống, sau đó phá hủy nó.

Khách hàng:

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

} 

Giá trị của this.doadUrl đã được đặt trước đó để trỏ đến api. Tôi đang sử dụng điều này để tải xuống tệp đính kèm, vì vậy tôi biết id, contentType và tên tệp: Tôi đang sử dụng một api MVC để trả lại tệp:

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

Lớp đính kèm trông như thế này:

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

Kho lưu trữ filerep trả về tệp từ cơ sở dữ liệu.

Hy vọng điều này sẽ giúp được ai đó :)


7

Đối với những người sử dụng Redux Pattern

Tôi đã thêm vào trình tiết kiệm tệp với tên @Hector Cuevas có tên trong câu trả lời của mình. Sử dụng Angular2 v. 2.3.1, tôi không cần thêm vào @ type / trình tiết kiệm tập tin.

Ví dụ sau đây là tải xuống một tạp chí dưới dạng PDF.

Các hành động tạp chí

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

Các hiệu ứng tạp chí

@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'))

Dịch vụ tạp chí

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

Dịch vụ HTTP

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

Bộ giảm tốc mặc dù điều này chỉ đặt các trạng thái chính xác được sử dụng trong ứng dụng của chúng tôi nhưng tôi vẫn muốn thêm nó vào để hiển thị mẫu hoàn chỉnh.

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

Tôi hy vọng điều này là hữu ích.


7

Tôi chia sẻ giải pháp giúp tôi (mọi cải tiến đều được đánh giá cao)

Trên dịch vụ của bạn 'pservice':

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

Phần thành phần:

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

Về phần thành phần, bạn gọi dịch vụ mà không đăng ký phản hồi. Việc đăng ký cho một danh sách đầy đủ các loại mime openEd xem: http://www.openoffice.org/framework/documentation/mimetypes/mimetypes.html


7

Sẽ tốt hơn nếu bạn cố gắng gọi phương thức mới bên trong bạn subscribe

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

downloadFile(data)Chức năng bên trong chúng ta cần thực hiệnblock, 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

Để tải xuống và hiển thị các tệp PDF , một đoạn mã tương tự được cắt như sau:

  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

Đây là điều tôi đã làm trong trường hợp của mình -

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

Giải pháp được tham khảo từ - tại đây


4

Cập nhật câu trả lời của Hector bằng cách sử dụng trình tiết kiệm tệp và HttpClient cho bước 2:

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

3

Tôi có một giải pháp để tải xuống từ angular 2 mà không bị hỏng, sử dụng spring mvc và angular 2

1st- kiểu trả về của tôi là: - ResponseEntity từ cuối java. Ở đây tôi đang gửi mảng byte [] có kiểu trả về từ bộ điều khiển.

Thứ 2 - bao gồm trình bảo vệ tệp trong không gian làm việc của bạn - trong trang chỉ mục là:

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

3- tại thành phần ts viết mã này:

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

                });

Điều này sẽ cung cấp cho bạn định dạng tệp xls. Nếu bạn muốn các định dạng khác thay đổi mediatype và tên tệp với phần mở rộng bên phải.


3

Tôi đã phải đối mặt với trường hợp tương tự ngày hôm nay, tôi đã phải tải xuống một tệp pdf dưới dạng tệp đính kèm (tệp không nên được hiển thị trong trình duyệt, nhưng thay vào đó được tải xuống). Để đạt được điều đó, tôi phát hiện ra rằng tôi phải lấy tệp trong Angular Blob, đồng thời, thêm một Content-Dispositiontiêu đề trong phản hồi.

Đây là cách đơn giản nhất tôi có thể nhận được (Angular 7):

Bên trong dịch vụ:

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

Sau đó, khi tôi cần tải xuống tệp trong một thành phần, tôi có thể chỉ cần:

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

CẬP NHẬT:

Đã xóa cài đặt tiêu đề không cần thiết khỏi dịch vụ


Nếu tôi sử dụng window.location.href thay vì window.open Chrome sẽ coi nó là nhiều tệp tải xuống.
DanO

điều này sẽ không hoạt động nếu bạn có mã thông báo xác thực cần thiết trong tiêu đề
garg10may

3

Các mã sau đây làm việc cho tôi

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

Tôi tìm thấy câu trả lời cho đến nay thiếu cái nhìn sâu sắc cũng như cảnh báo. Bạn có thể và nên theo dõi sự không tương thích với IE10 + (nếu bạn quan tâm).

Đây là ví dụ hoàn chỉnh với phần ứng dụng và phần dịch vụ sau. Lưu ý rằng chúng tôi đặt quan sát: "hồi đáp" để bắt tiêu đề cho tên tệp. Cũng lưu ý rằng tiêu đề Xử lý nội dung phải được đặt và hiển thị bởi máy chủ, nếu không thì Angular HttpClient hiện tại sẽ không chuyển nó vào. Tôi đã thêm một đoạn mã lõi dotnet cho bên dưới.

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

Lõi Dotnet, với Loại bỏ nội dung & 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

Đơn giản chỉ cần đặt urlnhư hrefnhư dưới đây.

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

Nó có hoạt động không? Tôi gặp lỗi ... "ERROR TypeError:" Truy cập vào tệp ': ///Doads/test.json' từ tập lệnh bị từ chối. ""
Jay

Cảm ơn bạn có thể chia sẻ làm thế nào để url của bạn trông như thế nào? Id nó tập tin giao thức hoặc http hoặc cái gì khác?
Jay

Đó là giao thức File.
Harunur Rashid


1

Bạn cũng có thể tải xuống một tệp trực tiếp từ mẫu của mình nơi bạn sử dụng thuộc tính tải xuống và để [attr.href]bạn có thể cung cấp giá trị thuộc tính từ thành phần. Giải pháp đơn giản này sẽ hoạt động trên hầu hết các trình duyệt.

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

Tham khảo: https://www.w3schools.com/tags/att_a_doad.asp


1
Chào mừng đến với SO! Vui lòng kiểm tra xem các chỉnh sửa (sắp chữ và ngữ pháp) của tôi có hữu ích không.
B - rian

0

Nếu bạn chỉ gửi các tham số đến một URL, bạn có thể thực hiện theo cách này:

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

trong dịch vụ nhận các tham số


0

Điều nàyCâu trả lời cho thấy rằng bạn không thể tải xuống các tệp trực tiếp bằng AJAX, chủ yếu vì lý do bảo mật. Vì vậy, tôi sẽ mô tả những gì tôi làm trong tình huống này,

01. Thêm hrefthuộc tính trong thẻ neo của bạn bên trong component.htmltệp,
ví dụ: -

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

02. Thực hiện tất cả các bước sau trong bạn component.tsđể vượt qua mức bảo mật và đưa hộp thoại lưu dưới dạng cửa sổ bật lên,
ví dụ: -

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

Lưu ý: Câu trả lời này sẽ hoạt động nếu bạn gặp lỗi "OK" với mã trạng thái 200


0

Chà, tôi đã viết một đoạn mã lấy cảm hứng từ nhiều câu trả lời ở trên, có thể dễ dàng hoạt động trong hầu hết các tình huống trong đó máy chủ gửi một tệp có tiêu đề xử lý nội dung, mà không cần bất kỳ cài đặt của bên thứ ba nào, ngoại trừ rxjs và góc cạnh.

Đầu tiên, làm thế nào để gọi mã từ tệp thành phần của bạn

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

Như bạn có thể thấy, về cơ bản, đó là khá nhiều cuộc gọi phụ trợ trung bình từ góc, với hai thay đổi

  1. Tôi đang quan sát phản ứng thay vì cơ thể
  2. Tôi đang rõ ràng về phản ứng là một blob

Khi tệp được tìm nạp từ máy chủ, về nguyên tắc, tôi ủy thác toàn bộ nhiệm vụ lưu tệp vào hàm trợ giúp, mà tôi giữ trong một tệp riêng và nhập vào bất kỳ thành phần nào tôi cần

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

Ở đó, không có tên tập tin GUID khó hiểu hơn! Chúng tôi có thể sử dụng bất kỳ tên nào mà máy chủ cung cấp, mà không cần phải chỉ định rõ ràng trong máy khách hoặc ghi đè tên tệp do máy chủ cung cấp (như trong ví dụ này). Ngoài ra, người ta có thể dễ dàng, nếu cần, thay đổi thuật toán trích xuất tên tệp từ bố trí nội dung cho phù hợp với nhu cầu của họ và mọi thứ khác sẽ không bị ảnh hưởng - trong trường hợp có lỗi trong quá trình trích xuất đó, nó sẽ chỉ chuyển 'null' như tên tệp.

Như một câu trả lời khác đã chỉ ra, IE luôn cần một số điều trị đặc biệt. Nhưng với cạnh crom xuất hiện trong một vài tháng, tôi sẽ không lo lắng về điều đó trong khi xây dựng các ứng dụng mới (hy vọng). Ngoài ra còn có vấn đề thu hồi URL, nhưng tôi không chắc lắm về điều đó, vì vậy nếu ai đó có thể giúp đỡ với điều đó trong các bình luận, điều đó thật tuyệt vời.


0

Nếu một tab mở và đóng mà không tải xuống bất cứ thứ gì, tôi đã thử làm theo với liên kết neo giả và nó đã hoạt động.

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);  }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.