วิธีดาวน์โหลดไฟล์โดยใช้ axios


124

ฉันใช้ axios สำหรับคำขอ http พื้นฐานเช่น GET และ POST และทำงานได้ดี ตอนนี้ฉันต้องสามารถดาวน์โหลดไฟล์ Excel ได้ด้วย เป็นไปได้ด้วย axios? ถ้ามีใครมีโค้ดตัวอย่างบ้าง ถ้าไม่ฉันจะใช้อะไรอีกในแอปพลิเคชัน React เพื่อทำเช่นเดียวกัน


เราสามารถใช้วิธีนี้เพื่อดาวน์โหลดไฟล์ excel stackoverflow.com/questions/57127361/…
Md. Nazrul Islam

คำตอบ:


107

เมื่อการตอบกลับมาพร้อมกับไฟล์ที่ดาวน์โหลดได้ส่วนหัวของการตอบกลับจะเป็นอย่างไร

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

สิ่งที่ทำได้คือสร้างคอมโพเนนต์แยกต่างหากซึ่งจะมี iframe ซ่อนอยู่

  import * as React from 'react';

  var MyIframe = React.createClass({

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

ตอนนี้คุณสามารถส่ง url ของไฟล์ที่ดาวน์โหลดได้เป็น prop ไปยังคอมโพเนนต์นี้ดังนั้นเมื่อคอมโพเนนต์นี้ได้รับ prop มันจะเรนเดอร์ใหม่และไฟล์จะถูกดาวน์โหลด

แก้ไข:คุณยังสามารถใช้โมดูลดาวน์โหลดไฟล์ js ลิงก์ไปยัง Github repo

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

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

หวังว่านี่จะช่วยได้ :)


1
ขอขอบคุณ. คุณบอกฉันได้ไหมว่านี่เป็นสไตล์อาแจ็กซ์ จะเป็นการดีที่จะไม่บล็อกเพจ
David Choi

ใช่เพจจะไม่ถูกบล็อก เมื่อคุณส่ง url เป็น prop ไปยังส่วนประกอบนั้นไฟล์จะถูกดาวน์โหลดโดยอัตโนมัติ คุณไม่จำเป็นต้องทำอะไรเลย
Hardik Modha

อีกหนึ่งคำถาม ในกรณีของฉันไฟล์ที่กำลังดาวน์โหลดถูกสร้างขึ้นแบบไดนามิกโดยมีการส่งผ่านพารามิเตอร์บางอย่าง ดังนั้นจึงไม่มีตำแหน่งที่สอดคล้องกันจริงๆ URL ที่ฉันส่งสำหรับสถานการณ์ประเภทนี้คืออะไร? ตัวอย่างเช่นถ้าฉันเรียก axios.post ('api / getmyexcelfile', params);
David Choi

ดังที่กล่าวไว้ในคำตอบนี้ . ในอ็อบเจ็กต์การตอบสนอง axios ภายในคำขอมีฟิลด์ชื่อ As responseURLซึ่งอาจเป็น URL ที่คุณต้องการ
Hardik Modha

1
ฉันทำตามนี้และสามารถดาวน์โหลดไฟล์ได้ แต่ไฟล์เสีย (ใช้งานไม่ได้) อย่างไรก็ตามหากฉันใช้การเปลี่ยนเส้นทาง (window.location.href) ไฟล์จะดาวน์โหลดและทำงานได้อย่างสมบูรณ์ ใครช่วยฉันด้วยได้ไหม ( stackoverflow.com/questions/56306008/… )
Thidasa Pankaja

126

วิธีแก้ปัญหาทั่วไป

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

ตรวจสอบนิสัยใจคอได้ที่https://gist.github.com/javilobo8/097c30a233786be52070986d8cdb1743

เครดิตเต็มไปที่: https://gist.github.com/javilobo8


12
ขอบคุณสำหรับวิธีแก้ปัญหา หมายเหตุสำหรับผู้อื่นเพียงเล็กน้อย: แม้ว่าวิธีนี้อาจใช้ได้กับกรณีการใช้งานจำนวนมาก แต่สำหรับไฟล์ขนาดใหญ่คุณจะไม่สามารถดูความคืบหน้าในการดาวน์โหลดได้ และจะใช้หน่วยความจำเพิ่มเติมในเบราว์เซอร์ ตามที่กล่าวถึงในโซลูชันอื่น ๆ แต่ไม่ได้สะกดออกวิธีการทั่วไปคือการใช้ส่วนหัว 'Content-Disposition: attachment' ดังนั้นเบราว์เซอร์จะถือว่าเป็นการดาวน์โหลดแบบเนทีฟ (ความคืบหน้าการดาวน์โหลดข้างต้น + ดาวน์โหลดลงดิสก์โดยตรง)
John Lee

ในฝั่งเซิร์ฟเวอร์แม้ว่าฉันจะตั้งค่าส่วนหัว Content-Desposition แต่ดูเหมือนจะไม่อนุญาตให้ดาวน์โหลด
huggie

4
ขอบคุณสำหรับสิ่งนี้. สงสัยว่าทำไมเนื้อหาไฟล์ไม่ปรากฏอย่างถูกต้อง ปรากฎว่าฉันหายไปresponseType: 'blob'
AliAvci

สิ่งนี้ไม่ดาวน์โหลดไฟล์เป็นการตอบสนองต่อหน่วยความจำก่อน (โดยที่เบราว์เซอร์ไม่แสดงความคืบหน้าในการดาวน์โหลดจริง ๆ ) เฉพาะเมื่อไฟล์ถูกดาวน์โหลดเป็นหยดในหน่วยความจำเบราว์เซอร์เท่านั้นที่จะพยายามบันทึกลงในไฟล์ดาวน์โหลด ..
Ricky -U

@ Ricky-U ใช่คุณพูดถูกคำขอ xhr จะถูกส่งและเมื่อการตอบกลับมาถึงมันจะถูกบัฟเฟอร์ในหน่วยความจำและเก็บไว้ในตัวแปรresponseเมื่อเสร็จสิ้น จากนั้นcreateObjectURLสร้าง URL ในเครื่องสำหรับข้อมูลนี้ที่ <a> สามารถนำทางไปได้
Viney

50

การดาวน์โหลดไฟล์ (โดยใช้ Axios และ Security)

สิ่งนี้ซับซ้อนยิ่งขึ้นเมื่อคุณต้องการดาวน์โหลดไฟล์โดยใช้ Axios และวิธีการรักษาความปลอดภัยบางอย่าง เพื่อป้องกันไม่ให้คนอื่นใช้เวลามากเกินไปในการหาคำตอบให้ฉันแนะนำคุณตลอดขั้นตอนนี้

คุณต้องทำ 3 สิ่ง:

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

ขั้นตอนเหล่านี้ส่วนใหญ่ทำได้ - แต่มีความซับซ้อนมากจากความสัมพันธ์ของเบราว์เซอร์กับ CORS หนึ่งขั้นในเวลา:

1. กำหนดค่าเซิร์ฟเวอร์ (HTTP) ของคุณ

เมื่อใช้การรักษาความปลอดภัยการขนส่ง JavaScript ที่ดำเนินการภายในเบราว์เซอร์จะสามารถ [โดยการออกแบบ] เข้าถึงเฉพาะส่วนหัว HTTP 6 รายการที่เซิร์ฟเวอร์ HTTP ส่งมา หากเราต้องการให้เซิร์ฟเวอร์แนะนำชื่อไฟล์สำหรับการดาวน์โหลดเราต้องแจ้งเบราว์เซอร์ว่า "ตกลง" เพื่อให้ JavaScript ได้รับสิทธิ์เข้าถึงส่วนหัวอื่น ๆ ที่จะส่งชื่อไฟล์ที่แนะนำ

ให้เราคิด - เพื่อประโยชน์ของการสนทนา - ที่เราต้องการเซิร์ฟเวอร์เพื่อส่งชื่อไฟล์ปัญหาภายในส่วนหัว HTTP ที่เรียกว่าX-แนะนำชื่อไฟล์ เซิร์ฟเวอร์ HTTP บอกเบราว์เซอร์ที่ว่ามันเป็นตกลงที่จะเปิดเผยส่วนหัวนี้เองที่ได้รับไปใช้ JavaScript / Axios กับส่วนหัวต่อไปนี้:

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

วิธีที่แน่นอนในการกำหนดค่าเซิร์ฟเวอร์ HTTP ของคุณเพื่อตั้งค่าส่วนหัวนี้จะแตกต่างกันไปในแต่ละผลิตภัณฑ์

โปรดดูhttps://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headersสำหรับคำอธิบายทั้งหมดและคำอธิบายโดยละเอียดของส่วนหัวมาตรฐานเหล่านี้

2. ใช้บริการฝั่งเซิร์ฟเวอร์

การใช้งานบริการฝั่งเซิร์ฟเวอร์ของคุณต้องดำเนินการ 2 อย่าง:

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

ซึ่งทำได้หลายวิธีขึ้นอยู่กับกลุ่มเทคโนโลยีที่คุณเลือก ฉันจะร่างตัวอย่างโดยใช้มาตรฐาน JavaEE 7 ซึ่งควรส่งรายงาน 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();
}

ขณะนี้บริการส่งเอกสารไบนารี (ในกรณีนี้คือรายงาน Excel) ตั้งค่าประเภทเนื้อหาที่ถูกต้องและยังส่งส่วนหัว HTTP ที่กำหนดเองซึ่งมีชื่อไฟล์ที่แนะนำเพื่อใช้เมื่อบันทึกเอกสาร

3. ติดตั้งตัวจัดการ Axios สำหรับเอกสารที่ได้รับ

มีข้อผิดพลาดบางประการที่นี่ดังนั้นให้แน่ใจว่ารายละเอียดทั้งหมดได้รับการกำหนดค่าอย่างถูกต้อง:

  1. บริการตอบสนองต่อ @GET (เช่น HTTP GET) ดังนั้นการเรียก axios ต้องเป็น 'axios.get (... )'
  2. เอกสารถูกส่งเป็นสตรีมไบต์ดังนั้นคุณต้องบอก axios ให้ถือว่าการตอบกลับเป็น HTML5 Blob (เช่นresponseType: 'blob' )
  3. ในกรณีนี้ไลบรารี JavaScript แบบประหยัดไฟล์จะใช้เพื่อเปิดกล่องโต้ตอบเบราว์เซอร์ให้เปิดขึ้น อย่างไรก็ตามคุณสามารถเลือกอย่างอื่นได้

การใช้งาน Skeleton Axios จะเป็นสิ่งที่อยู่ในแนวของ:

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

21
"FileSaver" คืออะไร?
Main Pal

6
เป็นไลบรารีสำหรับจัดการไฟล์ดาวน์โหลดgithub.com/eligrey/FileSaver.js/#filesaverjs
Radi

3
งานนี้ แต่ก็จะแนะนำให้ใช้หัวแทนcontent-disposition x-suggested-filename
Rosdi Kasim

14

โซลูชัน Axios.post กับ IE และเบราว์เซอร์อื่น ๆ

ฉันพบวิธีแก้ปัญหาที่น่าทึ่งที่นี่ แต่มักจะไม่คำนึงถึงปัญหาเกี่ยวกับเบราว์เซอร์ IE บางทีอาจจะช่วยประหยัดเวลาให้คนอื่นได้บ้าง

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

ตัวอย่างด้านบนใช้สำหรับไฟล์ excel แต่มีการเปลี่ยนแปลงเล็กน้อยสามารถนำไปใช้กับรูปแบบใดก็ได้

และบนเซิร์ฟเวอร์ฉันได้ทำสิ่งนี้เพื่อส่งไฟล์ excel

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

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

9

ฟังก์ชันในการเรียก API ด้วย axios:

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

เรียกใช้ฟังก์ชันจากนั้นดาวน์โหลดไฟล์ excel ที่คุณได้รับ:

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

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

เป็นรหัสจาวาสคริปต์ที่ง่ายมากในการเรียกใช้การดาวน์โหลดสำหรับผู้ใช้:

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

คุณไม่ต้องการ / ต้องการแกนสำหรับการดำเนินการนี้ ควรเป็นมาตรฐานที่จะปล่อยให้เบราว์เซอร์ทำสิ่งนั้น

หมายเหตุ:หากคุณต้องการการอนุญาตสำหรับการดาวน์โหลดสิ่งนี้อาจใช้ไม่ได้ ฉันค่อนข้างมั่นใจว่าคุณสามารถใช้คุกกี้เพื่ออนุญาตคำขอเช่นนี้ได้หากอยู่ในโดเมนเดียวกัน แต่ไม่ว่าในกรณีนี้อาจไม่ได้ผลทันที


ในฐานะที่เป็นไม่ว่าจะเป็นไปได้ ... ไม่ได้อยู่กับกลไกในการสร้างไฟล์ที่ดาวน์โหลดไม่


12
ส่วนหัวการให้สิทธิ์?
Ejaz Karim

ถ้าคุณต้องการส่งโทเค็นล่ะ?
user3808307

หากคุณควบคุมเซิร์ฟเวอร์คุณสามารถจัดเก็บโทเค็นการเข้าถึงเป็นคุกกี้และเบราว์เซอร์จะเพิ่มลงในคำขอใด ๆ ไปยังเซิร์ฟเวอร์ของคุณ medium.com/@ryanchenkie_40935/…
Multihunter

1
@CharithJayasanka ใช่ฉันเชื่ออย่างนั้น
Multihunter

2

เคล็ดลับคือการสร้างแท็กจุดยึดที่มองไม่เห็นในrender()และเพิ่มการตอบสนองrefเพื่อให้เกิดการคลิกเมื่อเรามีการตอบสนองแกน:

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

นี่เป็นวิธีการ: https://reactjs.org/docs/refs-and-the-dom.html คุณสามารถค้นหาความคิดที่คล้ายกันที่นี่: https://thewebtier.com/snippets/download-files-with-axios/


0

งานนี้สำหรับฉัน ฉันใช้โซลูชันนี้ใน reactJS

const requestOptions = {`enter code here`
method: 'GET',
headers: { 'Content-Type': 'application/json' }
};

fetch(`${url}`, requestOptions)
.then((res) => {
    return res.blob();
})
.then((blob) => {
    const href = window.URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = href;
    link.setAttribute('download', 'config.json'); //or any other extension
    document.body.appendChild(link);
    link.click();
})
.catch((err) => {
    return Promise.reject({ Error: 'Something Went Wrong', err });
})

-1

สำหรับคำขอ axios POST คำขอควรมีลักษณะดังนี้คีย์ที่นี่คือฟิลด์responseTypeและheaderต้องอยู่ในพารามิเตอร์ที่ 3 ของโพสต์ พารามิเตอร์ที่ 2 คือพารามิเตอร์แอ็พพลิเคชัน

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

คำตอบของฉันคือแฮ็กทั้งหมด - ฉันเพิ่งสร้างลิงก์ที่ดูเหมือนปุ่มและเพิ่ม URL ลงในนั้น

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

ฉันใช้VueJ ที่ยอดเยี่ยมดังนั้นคำอธิบายประกอบแปลก ๆ อย่างไรก็ตามโซลูชันนี้ไม่เชื่อเรื่องพระเจ้า แนวคิดนี้ใช้ได้กับการออกแบบที่ใช้ HTML

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.