Jak pobierać pliki przy użyciu axios

124

Używam axios do podstawowych żądań http, takich jak GET i POST, i działa dobrze. Teraz muszę mieć również możliwość pobierania plików Excela. Czy jest to możliwe w przypadku Axios? Jeśli tak, czy ktoś ma przykładowy kod? Jeśli nie, czego jeszcze mogę użyć w aplikacji React, aby zrobić to samo?

David Choi
źródło
Możemy użyć tego rozwiązania, aby pobrać plik Excela. stackoverflow.com/questions/57127361/…
Md. Nazrul Islam

Odpowiedzi:

107

Gdy odpowiedź jest dostarczana z plikiem do pobrania, nagłówki odpowiedzi będą wyglądać podobnie

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

Możesz utworzyć osobny komponent, który będzie zawierał ukrytą ramkę iframe.

  import * as React from 'react';

  var MyIframe = React.createClass({

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

Teraz możesz przekazać adres URL pliku do pobrania jako prop do tego komponentu, więc kiedy ten komponent otrzyma prop, wyrenderuje się ponownie i plik zostanie pobrany.

Edycja: Możesz także użyć modułu js-file-download . Link do repozytorium Github

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

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

Mam nadzieję że to pomoże :)

Hardik Modha
źródło
1
Dziękuję Ci. Czy możesz mi powiedzieć, czy to jest w stylu Ajax. Dobrze byłoby nie blokować strony.
David Choi
Tak, strona nie zostanie zablokowana. Kiedy podasz adres URL jako prop do tego komponentu, plik zostanie pobrany automatycznie. Nie musisz nic robić.
Hardik Modha
Jeszcze jedno pytanie. W moim przypadku pobierany plik jest tworzony dynamicznie z przekazanymi parametrami. Więc tak naprawdę nie ma spójnej lokalizacji. Jaki adres URL wysyłam w przypadku tego typu scenariusza? Na przykład, jeśli zadzwoniłem do axios.post ('api / getmyexcelfile', params);
David Choi
Jak wspomniano w tej odpowiedzi. W obiekcie odpowiedzi axios, wewnątrz żądania znajduje się pole o nazwie As responseURL, być może jest to adres URL, który chcesz.
Hardik Modha
1
Postępowałem zgodnie z tym i mogłem pobrać plik. Ale plik jest uszkodzony (nie działa). Jeśli jednak używam przekierowania (window.location.href), plik pobiera się i działa idealnie. Czy ktoś może mi w tym pomóc? ( stackoverflow.com/questions/56306008/… )
Thidasa Pankaja
126

Bardziej ogólne rozwiązanie

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

Sprawdź dziwactwa na https://gist.github.com/javilobo8/097c30a233786be52070986d8cdb1743

Pełne kredyty na: https://gist.github.com/javilobo8

Winorośl
źródło
12
Dziękuję za rozwiązanie. Kilka uwag dla innych: może to działać w wielu przypadkach, ale w przypadku dużych rozmiarów plików nie będzie można zobaczyć postępu pobierania. W przeglądarce zajmie to dodatkową pamięć. Jak wspomniano w innych rozwiązaniach, ale nie zostało to określone, ogólnym podejściem jest użycie nagłówka „Content-Disposition: załącznik;” więc przeglądarka potraktuje to jako pobieranie natywne (wspomniany wcześniej postęp pobierania + bezpośrednie pobieranie na dysk).
John Lee,
Po stronie serwera, nawet gdy ustawię nagłówek Content-Desposition, nie wydaje się, aby postęp pobierania był możliwy.
huggie
4
Dzięki za to. Zastanawiałem się, dlaczego zawartość pliku nie jest wyświetlana poprawnie. Okazuje się, że mi brakowałoresponseType: 'blob'
AliAvci
czy to nie pobiera pliku jako odpowiedzi najpierw do pamięci (bez przeglądarki faktycznie pokazującej postęp pobierania) tylko wtedy, gdy plik jest pobierany jako blob w pamięci, wtedy tylko przeglądarka będzie próbowała zapisać go do pliku do pobrania.
Ricky -U
@ Ricky-U Tak, masz rację, żądanie xhr zostanie wysłane, a gdy nadejdzie odpowiedź, zostanie ona buforowana w pamięci, a później przechowywana w zmiennej responsepo zakończeniu. Następnie createObjectURLtworzy lokalny adres URL do tych danych, do którego <a> może przejść.
Viney
50

Pobieranie plików (przy użyciu Axios i Security)

Jest to jeszcze bardziej skomplikowane, gdy chcesz pobierać pliki za pomocą Axios i niektórych środków bezpieczeństwa. Aby nikt inny nie spędzał zbyt wiele czasu na rozwiązywaniu tego, pozwól, że przeprowadzę cię przez to.

Musisz zrobić 3 rzeczy:

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

Te kroki są w większości wykonalne - ale są znacznie skomplikowane ze względu na relację przeglądarki do CORS. Krok po kroku:

1. Skonfiguruj serwer (HTTP)

Stosując zabezpieczenia transportu, JavaScript wykonywany w przeglądarce może [zgodnie z projektem] uzyskać dostęp tylko do 6 nagłówków HTTP faktycznie wysłanych przez serwer HTTP. Jeśli chcemy, aby serwer zasugerował nazwę pliku do pobrania, musimy poinformować przeglądarkę, że jest „OK”, aby JavaScript uzyskał dostęp do innych nagłówków, do których byłaby przesyłana sugerowana nazwa pliku.

Załóżmy - ze względu na dyskusję - że chcemy, aby serwer przesyłał sugerowaną nazwę pliku w nagłówku HTTP o nazwie X-Suggested-Filename . Serwer HTTP informuje przeglądarkę, że jest OK , aby wystawiać odebrany niestandardowy nagłówek z JavaScript / Axios z następującym nagłówkiem:

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

Dokładny sposób konfiguracji serwera HTTP w celu ustawienia tego nagłówka różni się w zależności od produktu.

Zobacz https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers, aby uzyskać pełne wyjaśnienie i szczegółowy opis tych standardowych nagłówków.

2. Zaimplementuj usługę po stronie serwera

Twoja implementacja usługi po stronie serwera musi teraz wykonywać 2 rzeczy:

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

Odbywa się to na różne sposoby, w zależności od wybranego stosu technologii. Naszkicuję przykład wykorzystując standard JavaEE 7, który powinien emitować raport w Excelu:

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

Usługa emituje teraz dokument binarny (w tym przypadku raport programu Excel), ustawia prawidłowy typ zawartości - a także wysyła niestandardowy nagłówek HTTP zawierający sugerowaną nazwę pliku do użycia podczas zapisywania dokumentu.

3. Zaimplementuj procedurę obsługi Axios dla otrzymanego dokumentu

Jest tu kilka pułapek, więc upewnijmy się, że wszystkie szczegóły są poprawnie skonfigurowane:

  1. Usługa odpowiada na @GET (tj. HTTP GET), więc wywołanie axios musi mieć postać „axios.get (...)”.
  2. Dokument jest przesyłany jako strumień bajtów, więc musisz powiedzieć Axios, aby traktował odpowiedź jako obiekt BLOB HTML5. (Tj. Typ odpowiedzi: „blob” ).
  3. W tym przypadku do otwierania okna przeglądarki używana jest biblioteka JavaScript do zapisywania plików. Możesz jednak wybrać inny.

Szkieletowa implementacja Axios wyglądałaby wtedy następująco:

 // 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);
    });
Lennart Jörelid
źródło
21
Co to jest „FileSaver”?
Main Pal
6
To biblioteka obsługująca pliki do pobrania, github.com/eligrey/FileSaver.js/#filesaverjs
Radi
3
To działa, ale zaleca się użycie content-dispositionnagłówka zamiast x-suggested-filename.
Rosdi Kasim
14

Rozwiązanie Axios.post z IE i innymi przeglądarkami

Znalazłem tutaj niesamowite rozwiązania. Ale często nie uwzględniają problemów z przeglądarką IE. Może zaoszczędzi to trochę czasu komuś innemu.

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

przykład powyżej dotyczy plików programu Excel, ale z niewielkimi zmianami można je zastosować do dowolnego formatu.

Zrobiłem to na serwerze, aby wysłać plik Excela.

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

response.addHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=exceptions.xlsx")
Alex
źródło
9

Funkcja do wywołania API z axios:

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

Wywołaj funkcję, a następnie pobierz otrzymany plik Excela:

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()
  })
roli roli
źródło
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.
})
Nitin.
źródło
8

Jest to bardzo prosty kod javascript, który uruchamia pobieranie dla użytkownika:

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

Nie chcesz / potrzebujesz axios do tej operacji; powinno być standardem, aby pozwolić przeglądarce zrobić to.

Uwaga: jeśli potrzebujesz autoryzacji do pobrania, może to nie działać. Jestem prawie pewien, że możesz użyć plików cookie do autoryzacji takiego żądania, pod warunkiem, że znajduje się w tej samej domenie, ale niezależnie od tego może to nie zadziałać natychmiast w takim przypadku.


A jeśli chodzi o to, czy jest to możliwe ... nie dzięki wbudowanemu mechanizmowi pobierania plików, nie .

Multihunter
źródło
12
Nagłówki autoryzacji?
Ejaz Karim
Co jeśli musisz wysłać token?
user3808307
Jeśli kontrolujesz serwer, możesz po prostu przechowywać token dostępu jako plik cookie, a przeglądarka doda go do każdego żądania na Twoim serwerze. medium.com/@ryanchenkie_40935/…
Multihunter
1
@CharithJayasanka Tak, myślę, że tak.
Multihunter
2

Sztuczka polega na zrobieniu niewidocznego tagu kotwicy w render()i dodaniu Reacta refumożliwiającego wywołanie kliknięcia, gdy otrzymamy odpowiedź 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>
        )
    }
}

Oto dokumentacja: https://reactjs.org/docs/refs-and-the-dom.html . Podobny pomysł znajdziesz tutaj: https://thewebtier.com/snippets/download-files-with-axios/ .

enjolrasyn
źródło
0

To zadziałało dla mnie. Wdrożyłem to rozwiązanie w 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 });
})
sharath shankar
źródło
-1

W przypadku żądania POST axios żądanie powinno wyglądać mniej więcej tak: Kluczowe jest tutaj to, że pola responseTypei headermuszą znajdować się w trzecim parametrze Post. Drugi parametr to parametry aplikacji.

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;
}
remondo
źródło
-4

Moja odpowiedź to totalny hack - właśnie utworzyłem link, który wygląda jak przycisk i dodałem do niego adres 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>

Używam doskonałych VueJ, stąd dziwne uwagi, jednak to rozwiązanie jest agnostykiem frameworka. Pomysł sprawdzi się w każdym projekcie opartym na HTML.

Anthony
źródło