Cách tải tệp xuống bằng axios


113

Tôi đang sử dụng axios cho các yêu cầu http cơ bản như GET và POST, và nó hoạt động tốt. Bây giờ tôi cũng cần có thể tải xuống các tệp Excel. Điều này có khả thi với tiên đề không? Nếu vậy có ai có một số mã mẫu? Nếu không, tôi có thể sử dụng gì khác trong ứng dụng React để làm điều tương tự?


Chúng ta có thể sử dụng giải pháp này để tải xuống tệp excel. stackoverflow.com/questions/57127361/…
Md. Nazrul Islam

Câu trả lời:


100

Khi phản hồi đi kèm với tệp có thể tải xuống, tiêu đề phản hồi sẽ giống như

Content-Disposition: "attachment;filename=report.xls"
Content-Type: "application/octet-stream" // or Content-type: "application/vnd.ms-excel"

Những gì bạn có thể làm là tạo một thành phần riêng biệt, thành phần này sẽ chứa một iframe ẩn.

  import * as React from 'react';

  var MyIframe = React.createClass({

     render: function() {
         return (
           <div style={{display: 'none'}}>
               <iframe src={this.props.iframeSrc} />
           </div>
         );
     }
  });

Bây giờ, bạn có thể chuyển url của tệp có thể tải xuống làm prop cho thành phần này, Vì vậy, khi thành phần này nhận được prop, nó sẽ hiển thị lại và tệp sẽ được tải xuống.

Chỉnh sửa: Bạn cũng có thể sử dụng mô-đun js-file-download . Liên kết đến repo Github

const FileDownload = require('js-file-download');

Axios({
  url: 'http://localhost/downloadFile',
  method: 'GET',
  responseType: 'blob', // Important
}).then((response) => {
    FileDownload(response.data, 'report.csv');
});

Hi vọng điêu nay co ich :)


1
Cảm ơn bạn. Bạn có thể cho tôi biết nếu điều này là trong phong cách ajax. Sẽ rất tốt nếu bạn không chặn trang.
David Choi,

Có, Trang sẽ không bị chặn. Khi bạn chuyển url làm chỗ dựa cho thành phần đó, tệp sẽ được tải xuống tự động. Bạn sẽ không cần phải làm gì cả.
Hardik Modha

Một câu hỏi nữa. Trong trường hợp của tôi, tệp đang được tải xuống được tạo động với một số tham số được truyền vào. Vì vậy, nó không thực sự có một vị trí nhất quán. Url tôi gửi cho loại kịch bản này là gì? Ví dụ: nếu tôi gọi axios.post ('api / getmyexcelfile', params);
David Choi

Như đã đề cập trong câu trả lời này . Trong đối tượng phản hồi axios, bên trong yêu cầu có một trường tên là As responseURL, có thể đây là URL mà bạn muốn.
Hardik Modha

1
Tôi đã làm theo điều này và có thể tải xuống tệp. Nhưng tệp bị hỏng (không hoạt động). Tuy nhiên, nếu tôi sử dụng chuyển hướng (window.location.href), tệp sẽ tải xuống và hoạt động hoàn hảo. Xin vui lòng giúp tôi việc này? ( stackoverflow.com/questions/56306008/… )
Thidasa Pankaja

118

Một giải pháp chung hơn

axios({
  url: 'http://api.dev/file-download', //your url
  method: 'GET',
  responseType: 'blob', // important
}).then((response) => {
   const url = window.URL.createObjectURL(new Blob([response.data]));
   const link = document.createElement('a');
   link.href = url;
   link.setAttribute('download', 'file.pdf'); //or any other extension
   document.body.appendChild(link);
   link.click();
});

Xem những điều kỳ quặc tại https://gist.github.com/javilobo8/097c30a233786be52070986d8cdb1743

Toàn bộ tín dụng cho: https://gist.github.com/javilobo8


11
Cảm ơn bạn cho giải pháp. Chỉ một vài lưu ý cho những người khác: Mặc dù điều này có thể hiệu quả với nhiều trường hợp sử dụng, nhưng đối với kích thước tệp lớn, bạn sẽ không thể thấy tiến trình tải xuống. Và nó sẽ tốn thêm bộ nhớ trong trình duyệt. Như được đề cập đến trong các giải pháp khác, nhưng không được viết chính tả, cách tiếp cận chung là sử dụng tiêu đề 'Nội dung-Bố trí: tệp đính kèm;' vì vậy trình duyệt sẽ coi nó như một bản tải xuống gốc (tiến trình tải xuống đã nói ở trên + tải trực tiếp vào đĩa).
John Lee

Ở phía máy chủ, ngay cả khi tôi đặt tiêu đề Nội dung-Desposition, nó dường như không cho phép tiến trình tải xuống.
huggie

4
Cám ơn vì cái này. Đang tự hỏi tại sao nội dung tệp không xuất hiện chính xác. Hóa ra tôi đã mất tíchresponseType: 'blob'
AliAvci

Điều này không tải xuống tệp dưới dạng phản hồi trước cho bộ nhớ (không có trình duyệt thực sự hiển thị tiến trình tải xuống) chỉ khi tệp được tải xuống dưới dạng một đốm màu trong bộ nhớ thì chỉ có trình duyệt mới cố gắng lưu nó vào tệp tải xuống ..
Ricky -U

@ Ricky-U Vâng, bạn nói đúng, yêu cầu xhr sẽ được gửi và khi phản hồi đến, nó sẽ được lưu vào bộ nhớ đệm và sau đó, được lưu trữ trong biến responsekhi hoàn thành. Sau đó, createObjectURLtạo một url cục bộ cho dữ liệu này mà <a> có thể điều hướng đến.
Viney

48

Tải xuống tệp (sử dụng Axios và Security)

Điều này thực sự còn phức tạp hơn khi bạn muốn tải tệp xuống bằng Axios và một số phương tiện bảo mật. Để tránh cho bất kỳ ai khác mất quá nhiều thời gian để tìm ra điều này, hãy để tôi hướng dẫn bạn qua điều này.

Bạn cần làm 3 điều:

1. Configure your server to permit the browser to see required HTTP headers
2. Implement the server-side service, and making it advertise the correct file type for the downloaded file.
3. Implementing an Axios handler to trigger a FileDownload dialog within the browser

Các bước này hầu hết đều có thể làm được - nhưng phức tạp đáng kể bởi mối quan hệ của trình duyệt với CORS. Một bước tại một thời điểm:

1. Định cấu hình máy chủ (HTTP) của bạn

Khi sử dụng bảo mật truyền tải, JavaScript thực thi trong trình duyệt [theo thiết kế] chỉ có thể truy cập 6 trong số các tiêu đề HTTP thực sự được gửi bởi máy chủ HTTP. Nếu chúng tôi muốn máy chủ đề xuất tên tệp để tải xuống, chúng tôi phải thông báo cho trình duyệt rằng "OK" để JavaScript được cấp quyền truy cập vào các tiêu đề khác nơi tên tệp được đề xuất sẽ được chuyển.

Chúng ta hãy giả sử - để thảo luận - rằng chúng ta muốn máy chủ truyền tên tệp được đề xuất trong tiêu đề HTTP được gọi là X-Suggested-Filename . Các máy chủ HTTP cho trình duyệt đó là OK để lộ tiêu đề tùy chỉnh nhận này để JavaScript / Axios với tiêu đề sau đây:

Access-Control-Expose-Headers: X-Suggested-Filename

Cách chính xác để định cấu hình máy chủ HTTP của bạn để đặt tiêu đề này khác nhau giữa các sản phẩm.

Xem https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers để biết giải thích đầy đủ và mô tả chi tiết về các tiêu đề chuẩn này.

2. Triển khai dịch vụ phía máy chủ

Việc triển khai dịch vụ phía máy chủ của bạn bây giờ phải thực hiện 2 điều:

1. Create the (binary) document and assign correct ContentType to the response
2. Assign the custom header (X-Suggested-Filename) containing the suggested file name for the client

Điều này được thực hiện theo nhiều cách khác nhau tùy thuộc vào công nghệ bạn đã chọn. Tôi sẽ phác thảo một ví dụ bằng cách sử dụng tiêu chuẩn JavaEE 7 sẽ tạo ra một báo cáo Excel:

@GET
@Path("/report/excel")
@Produces("application/vnd.ms-excel")
public Response getAllergyAndPreferencesReport() {

    // Create the document which should be downloaded
    final byte[] theDocumentData = .... 

    // Define a suggested filename
    final String filename = ... 

    // Create the JAXRS response
    // Don't forget to include the filename in 2 HTTP headers: 
    //
    // a) The standard 'Content-Disposition' one, and
    // b) The custom 'X-Suggested-Filename'  
    //
    final Response.ResponseBuilder builder = Response.ok(
            theDocumentData, "application/vnd.ms-excel")
            .header("X-Suggested-Filename", fileName);
    builder.header("Content-Disposition", "attachment; filename=" + fileName);

    // All Done.
    return builder.build();
}

Dịch vụ hiện phát tài liệu nhị phân (trong trường hợp này là báo cáo Excel), đặt loại nội dung chính xác - và cũng gửi một tiêu đề HTTP tùy chỉnh có chứa tên tệp được đề xuất để sử dụng khi lưu tài liệu.

3. Triển khai trình xử lý Axios cho tài liệu Đã nhận

Có một số cạm bẫy ở đây, vì vậy hãy đảm bảo tất cả các chi tiết đều được định cấu hình chính xác:

  1. Dịch vụ trả lời @GET (tức là HTTP GET), vì vậy lệnh gọi axios phải là 'axios.get (...)'.
  2. Tài liệu được truyền dưới dạng một luồng byte, vì vậy bạn phải yêu cầu axios coi phản hồi như một HTML5 Blob. (Tức là responseType: 'blob' ).
  3. Trong trường hợp này, thư viện JavaScript trình tiết kiệm tệp được sử dụng để mở hộp thoại trình duyệt. Tuy nhiên, bạn có thể chọn cái khác.

Việc triển khai Axios khung sau đó sẽ là một cái gì đó dọc theo các dòng:

 // Fetch the dynamically generated excel document from the server.
 axios.get(resource, {responseType: 'blob'}).then((response) => {

    // Log somewhat to show that the browser actually exposes the custom HTTP header
    const fileNameHeader = "x-suggested-filename";
    const suggestedFileName = response.headers[fileNameHeader];'
    const effectiveFileName = (suggestedFileName === undefined
                ? "allergierOchPreferenser.xls"
                : suggestedFileName);
    console.log("Received header [" + fileNameHeader + "]: " + suggestedFileName
                + ", effective fileName: " + effectiveFileName);

    // Let the user save the file.
    FileSaver.saveAs(response.data, effectiveFileName);

    }).catch((response) => {
        console.error("Could not Download the Excel report from the backend.", response);
    });

20
"FileSaver" là gì?
Main Pal

6
Đó là một thư viện để xử lý các file tải về, github.com/eligrey/FileSaver.js/#filesaverjs
Radi

2
Điều này hoạt động, nhưng bạn nên sử dụng content-dispositiontiêu đề thay vì x-suggested-filename.
Rosdi Kasim

13

Giải pháp Axios.post với IE và các trình duyệt khác

Tôi đã tìm thấy một số giải pháp đáng kinh ngạc ở đây. Nhưng họ thường không tính đến các vấn đề với trình duyệt IE. Có lẽ nó sẽ tiết kiệm thời gian cho người khác.

 axios.post("/yourUrl"
                , data,
                {responseType: 'blob'}
            ).then(function (response) {
                    let fileName = response.headers["content-disposition"].split("filename=")[1];
                    if (window.navigator && window.navigator.msSaveOrOpenBlob) { // IE variant
                        window.navigator.msSaveOrOpenBlob(new Blob([response.data], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'}),
                            fileName);
                    } else {
                        const url = window.URL.createObjectURL(new Blob([response.data], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'}));
                        const link = document.createElement('a');
                        link.href = url;
                        link.setAttribute('download', response.headers["content-disposition"].split("filename=")[1]);
                        document.body.appendChild(link);
                        link.click();
                    }
                }
            );

Ví dụ trên dành cho các tệp excel, nhưng với một chút thay đổi có thể được áp dụng cho bất kỳ định dạng nào.

Và trên máy chủ, tôi đã thực hiện việc này để gửi một tệp excel.

response.contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"

response.addHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=exceptions.xlsx")

8
        axios.get(
            '/app/export'
        ).then(response => {    
            const url = window.URL.createObjectURL(new Blob([response]));
            const link = document.createElement('a');
            link.href = url;
            const fileName = `${+ new Date()}.csv`// whatever your file name .
            link.setAttribute('download', fileName);
            document.body.appendChild(link);
            link.click();
            link.remove();// you need to remove that elelment which is created before.
})

8

Hàm thực hiện lệnh gọi API với axios:

  function getFileToDownload (apiUrl) {
     return axios.get(apiUrl, {
       responseType: 'arraybuffer',
       headers: {
         'Content-Type': 'application/json'
       }
     })
  }

Gọi hàm và sau đó tải xuống tệp excel bạn nhận được:

getFileToDownload('putApiUrlHere')
  .then (response => {
      const type = response.headers['content-type']
      const blob = new Blob([response.data], { type: type, encoding: 'UTF-8' })
      const link = document.createElement('a')
      link.href = window.URL.createObjectURL(blob)
      link.download = 'file.xlsx'
      link.click()
  })

6

Đó là mã javascript rất đơn giản để kích hoạt tải xuống cho người dùng:

window.open("<insert URL here>")

Bạn không muốn / cần tiên đề cho hoạt động này; nó phải là tiêu chuẩn để chỉ để cho trình duyệt làm việc đó.

Lưu ý: Nếu bạn cần ủy quyền để tải xuống thì điều này có thể không hoạt động. Tôi khá chắc rằng bạn có thể sử dụng cookie để cho phép một yêu cầu như thế này, miễn là nó nằm trong cùng một miền, nhưng bất kể, điều này có thể không hoạt động ngay lập tức trong trường hợp như vậy.


Về việc liệu nó có thể ... không với cơ chế tải xuống tệp tích hợp sẵn, không .


12
Tiêu đề ủy quyền?
Ejaz Karim

Điều gì xảy ra nếu bạn cần gửi mã thông báo?
user3808307

Nếu bạn kiểm soát máy chủ, thì bạn có thể chỉ cần lưu trữ mã thông báo truy cập dưới dạng cookie và trình duyệt sẽ thêm nó vào bất kỳ yêu cầu nào tới máy chủ của bạn. medium.com/@ryanchenkie_40935/…
Multihunter

Điều này chỉ có thể được sử dụng nếu nó là một GET phải không?
Charith Jayasanka

1
@CharithJayasanka Vâng, tôi tin như vậy.
Multihunter

2

Mẹo là tạo một thẻ neo vô hình trong render()và thêm một React refcho phép kích hoạt một nhấp chuột khi chúng tôi có phản hồi axios:

class Example extends Component {
    state = {
        ref: React.createRef()
    }

    exportCSV = () => {
        axios.get(
            '/app/export'
        ).then(response => {
            let blob = new Blob([response.data], {type: 'application/octet-stream'})
            let ref = this.state.ref
            ref.current.href = URL.createObjectURL(blob)
            ref.current.download = 'data.csv'
            ref.current.click()
        })
    }

    render(){
        return(
            <div>
                <a style={{display: 'none'}} href='empty' ref={this.state.ref}>ref</a>
                <button onClick={this.exportCSV}>Export CSV</button>
            </div>
        )
    }
}

Đây là tài liệu: https://reactjs.org/docs/refs-and-the-dom.html . Bạn có thể tìm thấy một ý tưởng tương tự tại đây: https://thewebtier.com/snippets/download-files-with-axios/ .


-1

Đối với yêu cầu POST của axios, yêu cầu phải giống như sau: Điều quan trọng ở đây là các trường responseTypeheaderphải nằm trong tham số thứ 3 của Post. Tham số thứ 2 là tham số ứng dụng.

export const requestDownloadReport = (requestParams) => async dispatch => { 
  let response = null;
  try {
    response = await frontEndApi.post('createPdf', {
      requestParams: requestParams,
    },
    {
      responseType: 'arraybuffer', // important...because we need to convert it to a blob. If we don't specify this, response.data will be the raw data. It cannot be converted to blob directly.
      headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/pdf'
      }
  });          
  }
  catch(err) {
    console.log('[requestDownloadReport][ERROR]', err);
    return err
  }

  return response;
}

-4

Câu trả lời của tôi là một cuộc tấn công hoàn toàn - Tôi vừa tạo một liên kết trông giống như một nút và thêm URL vào đó.

<a class="el-button"
  style="color: white; background-color: #58B7FF;"
  :href="<YOUR URL ENDPOINT HERE>"
  :download="<FILE NAME NERE>">
<i class="fa fa-file-excel-o"></i>&nbsp;Excel
</a>

Tôi đang sử dụng các VueJ tuyệt vời do đó có các chú thích kỳ lạ, tuy nhiên, giải pháp này là bất khả tri về khung. Ý tưởng sẽ hoạt động cho bất kỳ thiết kế dựa trên HTML nào.

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.