Laden Sie ein ReactJS-Objekt als Datei herunter

Lesezeit: 8 Minuten

Benutzeravatar von fryeguy
fryeguy

Ich baue eine Anwendung mit einem ReactJS-Frontend, das eine Verbindung zu einem Express-API-Server herstellt. Aufrufe an die API erfolgen mit Ajax.

In einer meiner Ansichten wird eine Tabelle mit “Export”-Links in jeder Zeile geladen. Die Export-Links führen zu einer React-Route, die einen API-Endpunkt aufruft, der eine CSV-Datei zum Herunterladen bereitstellt.

Wenn ich den API-Endpunkt direkt mit einer gültigen Anfrage (außerhalb der React-App) erreiche, wird ein Dateidownload in meinem Browser initiiert. Perfekt! Wenn Sie jedoch dem Export-Link auf der React-Seite folgen, wird versucht, die Ansicht zu laden, in der der Aufruf der API erfolgt. Die Tabelle verschwindet aus der Ansicht und wird durch den Dateiinhalt ersetzt (um zu beweisen, dass ich die Daten habe), aber es wird keine Datei heruntergeladen.

Kann ich einen Download des Inhalts des Antwortobjekts als Datei erzwingen? Könnte dies im Ajax-Erfolgsrückruf stattfinden? Ich habe einen Versuch mit Javascript gemacht, aber ich kämpfe mit dem virtuellen DOM von React … Ich nehme an, das muss ziemlich einfach sein, aber ich bin ratlos.

BEARBEITEN: Kommentare von @Blex haben mir geholfen, dieses Problem zu lösen! Die Lösung wird dem Code-Snippet hinzugefügt…

Hier ist die JSX, die die Daten empfängt:

module.exports = React.createClass({

    mixins: [Router.State],
    getInitialState: function() {
        return {
            auth: getAuthState(),
            export: [],
            passedParams: this.getParams()
        };
    },

    componentDidMount: function(){
        $.ajax({
            type: 'GET',
            url: ''+ API_URL +'/path/to/endpoint'+ this.state.passedParams.id +'/export',
            dataType: 'text',
            headers: {
                'Authorization': 'Basic ' + this.state.auth.base + ''
            },
            success: function (res) {
                // can I force a download of res here?
                console.log('Export Result Success -- ', res);
                if(this.isMounted()){
                    console.log('Export Download Data -- ', res);
                    this.setState({export: res[1]});
                    // adding the next three lines solved my problem
                    var data = new Blob([res], {type: 'text/csv'});
                    var csvURL = window.URL.createObjectURL(data);
                    //window.open(csvURL);
                    // then commenting out the window.open & replacing
                    // with this allowed a file name to be passed out
                    tempLink = document.createElement('a');
                    tempLink.href = csvURL;
                    tempLink.setAttribute('download', 'filename.csv');
                    tempLink.click();
                }
            }.bind(this),
            error: function (data) {
                console.log('Export Download Result Error -- ', data);
            }
        });
    },

    render: function(){
        console.log('exam assignment obj -- ', this.state.passedParams.name);
        var theFileContents = this.state.export;
            return(
            <div className="row test-table">
                <table className="table" >
                    <tr className="test-table-headers">
                    {theFileContents} // this loads the contents
                    // can I auto download theFileContents?
                    </tr>
                </table>
            </div>
            )
    }
});

  • Sie können es so machen: jsfiddle.net/8f2ah406 Das einzige Problem hier ist, dass der Dateiname und die Erweiterung manuell festgelegt werden müssen, wenn Sie dazu aufgefordert werden. Ansonsten eben window.open(/* URL of the Ajax request */);

    – Blex

    3. Juli 2015 um 22:12 Uhr


  • Das hat perfekt funktioniert! Jetzt muss ich nur noch herausfinden, wie ich den Dateinamen automatisch festlegen kann.

    – fryeguy

    3. Juli 2015 um 22:22 Uhr

  • Siehe auch: stackoverflow.com/questions/14964035/…

    – Peeyush Kushwaha

    3. Juli 2015 um 22:24 Uhr


  • Los geht’s: jsfiddle.net/unmf5dp0

    – Blex

    3. Juli 2015 um 22:28 Uhr

  • Danke @PeeyushKushwaha für den Link. In regulärem Javascript denke ich, dass dies gut funktioniert. Ich arbeite jedoch in einer React JS-Umgebung, die das Javascript-DOM vom Benutzer abstrahiert. Das Konzept eines virtuellen DOM ist mir noch ziemlich fremd und macht viele Aufgaben kompliziert.

    – fryeguy

    4. Juli 2015 um 3:59 Uhr

Benutzeravatar von fryeguy
fryeguy

Durch Hinzufügen des folgenden Codes basierend auf Kommentaren von @blex funktionierte der Dateidownload. Um es im Kontext zu sehen, werfen Sie einen Blick auf den Erfolgsrückruf in der Frage.

var data = new Blob([res], {type: 'text/csv'});
var csvURL = window.URL.createObjectURL(data);
tempLink = document.createElement('a');
tempLink.href = csvURL;
tempLink.setAttribute('download', 'filename.csv');
tempLink.click();

  • Wenn Sie tempLink an den Körper anhängen, funktioniert es auch in Firefox. Bei Safari bin ich mir nicht sicher.

    – jstice4all

    3. Oktober 2018 um 15:38 Uhr

  • Ich würde diesen Ansatz zumindest nicht für die Reactjs verwenden, da dadurch der gesamte Dombaum neu generiert wird.

    – Mahesh

    12. September 2019 um 5:00 Uhr

  • hinzufügen var zum tempLink Variable

    – sharun kk

    19. Januar 2021 um 12:16 Uhr

  • Wird diese Methode eine Datei irgendeiner Art von realer Größe verarbeiten? Ich meine, ein Objekt als URL erstellen? Wird das wirklich eine 4-GB-Datei verarbeiten?

    – DiggyJohn

    12. Mai 2021 um 19:14 Uhr

  • Der ursprüngliche Beitrag war vor fast sechs Jahren, aber wenn ich mich richtig erinnere, war der Sinn dessen, was ich erreichen wollte, eine Option zum Herunterladen der auf der Seite angezeigten Daten im CSV-Format bereitzustellen. Ich nehme an, wenn ich die Daten auf der Seite anzeigen könnte, sehe ich keinen Grund, warum eine große Datei fehlschlagen würde. Ich bezweifle jedoch, dass die Daten, mit denen ich gearbeitet habe, über mehrere MB hinausgingen, geschweige denn über ein GB. Vielleicht einen Testfall ausprobieren.

    – fryeguy

    19. Mai 2021 um 2:21 Uhr


Benutzeravatar von Neo
Neo

Ich habe ein Paket verwendet jsonexport in meiner React-App und jetzt kann ich die CSV-Datei auf einen Link-Klick herunterladen. Folgendes habe ich getan:

.
.
import React, {useState,useEffect} from 'react';// I am using React Hooks
import * as jsonexport from "jsonexport/dist";
.
.
.
const [filedownloadlink, setFiledownloadlink] = useState("");//To store the file download link

.
.
.

Erstellen Sie eine Funktion, die Daten für CSV bereitstellt. Es kann sich auch um einen Rückruf von einer Netzwerkanfrage handeln. Wenn diese Methode aufgerufen wird, setzt sie value in filedownloadlink Zustand.

function handleSomeEvent(){
var contacts = [{
        name: 'Bob',
        lastname: 'Smith'
    },{
        name: 'James',
        lastname: 'David'
    },{
        name: 'Robert',
        lastname: 'Miller' 
    },{
        name: 'David',
        lastname: 'Martin'
    }];

    jsonexport(contacts,function(err, csv){
        if(err) return console.log(err);
        var myURL = window.URL || window.webkitURL //window.webkitURL works in Chrome and window.URL works in Firefox
        var csv = csv;  
        var blob = new Blob([csv], { type: 'text/csv' });  
        var csvUrl = myURL.createObjectURL(blob);
        setFiledownloadlink(csvUrl);
    });
}

Verwenden Sie in der Render-Funktion so etwas:

{filedownloadlink &&<a download="UserExport.csv" href={filedownloadlink}>Download</a>}

Der obige Link wird sichtbar, wenn filedownloadlink hat einige Daten zum Herunterladen.

Benutzeravatar von Mohamed Iqzas
Mohamed Iqzas

Fügen Sie den folgenden Code für zukünftige Referenzzwecke hinzu. Dazu gehören einige zusätzliche Überprüfungen der Browserkompatibilität und zusätzlicher Code zur Aufnahme von IE10+.

/**
 * Take a blob and force browser to click a link and save it from a download path
 * log out timing
 *
 * @param {Blob}
 * @method saveFile
 */
function saveFile(blob) {
    const uniqTime = new Date().getTime();
    const filename = `my_file_${uniqTime}`;

    if (navigator.msSaveBlob) { // IE 10+
        console.info('Starting call for ' + 'ie download');
        const csvFormatTimeStart = new Date().getTime();

        const ieFilename = `${filename}.csv`;
        navigator.msSaveBlob(blob, ieFilename);

        const csvFormatTimeEnd = new Date().getTime();
        const csvFormatTime = csvFormatTimeEnd - csvFormatTimeStart;
        console.log('ie download takes ' + csvFormatTime + ' ms to run');
    } else {
        console.info('Starting call for ' + 'regular download');
        const csvFormatTimeStart = new Date().getTime();
        let link = document.createElement("a");
        if (link.download !== undefined) { // feature detection
            // Browsers that support HTML5 download attribute
            var 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);
        }

        const csvFormatTimeEnd = new Date().getTime();
        const csvFormatTime = csvFormatTimeEnd - csvFormatTimeStart;
        console.log('regular download takes ' + csvFormatTime + ' ms to run');
    }

    clickEnd = new Date().getTime();
    console.log('The whole process took: ' + (clickEnd - clickStart) + ' ms');
}

Kredit sollte gehen Dieser Artikel.

morganneys Benutzeravatar
morganney

Dies ist eine React-Funktionskomponente, die ich kürzlich für Downloads verwendet habe. Ziemlich einfache Konvertierung in TypeScript.

import React, { useEffect, useRef } from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'

const Link = styled.a`
  display: none;
`
const Downloader = ({ blob, filename, onDownload }) => {
  const link = useRef(null)
  const url = URL.createObjectURL(blob)

  useEffect(() => {
    link.current.click()
    onDownload()

    return () => {
      URL.revokeObjectURL(url)
    }
  }, [url, onDownload])

  return (
    <Link ref={link} href={url} download={filename}>
      Table export
    </Link>
  )
}

Downloader.propTypes = {
  blob: PropTypes.object.isRequired,
  filename: PropTypes.string.isRequired,
  onDownload: PropTypes.func.isRequired
}

export { Downloader }

Hier ist ein kurzes Beispiel, wie es verwendet werden kann:

const { useRef, useEffect, useCallback, useState } = React
const { styled } = window
const root = ReactDOM.createRoot(document.getElementById('root'))
const Link = styled.a`
  display: none;
`
const Downloader = ({ blob, filename, onDownload }) => {
  const link = useRef(null)
  const url = URL.createObjectURL(blob)

  useEffect(() => {
    console.log('download url', url)
    link.current.click()
    onDownload()

    return () => {
      URL.revokeObjectURL(url)
    }
  }, [url, onDownload])

  return (
    <Link ref={link} href={url} download={filename}>
      Table export
    </Link>
  )
}
const App = () => {
  const [download, setDownload] = useState(false)
  const [file, setFile] = useState(null)
  const handleFileChange = useCallback((evt) => {
    setFile(evt.target.files[0])
  }, [setFile])
  const handleDownload = useCallback(() => {
    setDownload(true)
  }, [setDownload])
  const onDownload = useCallback(() => {
    console.log('download finished')
    setDownload(false)
  }, [setDownload])
 
  return (
    <div>
      <form>
        <input type="file" name="some-file" onChange={handleFileChange} />
      </form>
      {file && (
        <button onClick={handleDownload}>Download file</button>
      )}
      {file && download && (
        <Downloader blob={file} filename={file.name} onDownload={onDownload} />
      )}
    </div>
  )
}

root.render(<App />)
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/react-is/umd/react-is.production.min.js"></script>
<script src="https://unpkg.com/styled-components/dist/styled-components.min.js"></script>
<div id="root"></div>

Benutzeravatar von mattsmith5
Mattschmied5

So mache ich es in React Function-Komponenten:

const [productTemplateUrl, setProductTemplateUrl] = useState<string | undefined>(undefined,);
const downloadRef = useRef<HTMLAnchorElement>(null);

const getTemplate = async () => {
  const res = await getProductTemplate();
  const url = window.URL.createObjectURL(new Blob([res]));
  setProductTemplateUrl(url);
  if (downloadRef != null) {
    downloadRef?.current?.click();
  }
};

<a
  style={{ display: 'none' }}
  download="product-template.csv"
  href={productTemplateUrl}
  ref={downloadRef}
/>

  • Vielleicht möchten Sie eine in Betracht ziehen useEffect. Das habe ich schon einmal verwendet: stackoverflow.com/a/74105323/258174

    – Morganney

    18. Oktober um 3:22

  • Vielleicht möchten Sie eine in Betracht ziehen useEffect. Das habe ich schon einmal verwendet: stackoverflow.com/a/74105323/258174

    – Morganney

    18. Oktober um 3:22

1431490cookie-checkLaden Sie ein ReactJS-Objekt als Datei herunter

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy