Wie kann ich eine Datei mit window.fetch herunterladen?


74

Was kann ich im folgenden thenBlock tun, wenn ich eine Datei herunterladen möchte ?

function downloadFile(token, fileId) {
  let url = `https://www.googleapis.com/drive/v2/files/${fileId}?alt=media`;
  return fetch(url, {
    method: 'GET',
    headers: {
      'Authorization': token
    }
  }).then(...);
}

Beachten Sie, dass sich die Codes clientseitig befinden.


1
Was ist mit dem Hinzufügen eines Download-Attributs zu einem Link, der die URL hat https://www.googleapis.com/drive/v2/files/${fileId}?alt=media
Arjun

@Arjun Wie fügst du das Token zum Header deiner Anfrage hinzu?
anhtv13

@ anhtv13 Es tut mir leid, ich verstehe deine Frage nicht. Mit meinem ursprünglichen Kommentar schlug ich vor, ein <a>Element mit einem downloadAttribut zu erstellen und einen Klick auf dieses Element zu simulieren, alles unter Verwendung von JavaScript. Siehe Zibris Antwort (die übrigens weit nach meinem Kommentar veröffentlicht wurde).
Arjun

Die API ist sicher, Sie können nicht einfach zur URL gehen und herunterladen, sondern müssen ein Token an den Header übergeben. So etwas wie "'Authorization": "Bearer" + <token>. Wie fügen Sie in dieser Situation das Token zum Header hinzu?
anhtv13

Antworten:


73

EDIT : Syg Antwort ist besser. Verwenden Sie einfach die downloadjs Bibliothek.

Die Antwort, die ich gegeben habe, funktioniert gut in Chrome, aber in Firefox und IE benötigen Sie eine andere Variante dieses Codes. Es ist besser, dafür die Bibliothek zu verwenden.


Ich hatte ein ähnliches Problem (ich muss den Autorisierungsheader übergeben, um eine Datei herunterzuladen, damit diese Lösung nicht hilft).

Basierend auf dieser Antwort können Sie createObjectURLden Browser veranlassen, eine von der Fetch-API heruntergeladene Datei zu speichern.

getAuthToken()
    .then(token => {
        fetch("http://example.com/ExportExcel", {
            method: 'GET',
            headers: new Headers({
                "Authorization": "Bearer " + token
            })
        })
        .then(response => response.blob())
        .then(blob => {
            var url = window.URL.createObjectURL(blob);
            var a = document.createElement('a');
            a.href = url;
            a.download = "filename.xlsx";
            document.body.appendChild(a); // we need to append the element to the dom -> otherwise it will not work in firefox
            a.click();    
            a.remove();  //afterwards we remove the element again         
        });
    });

@ David Ich habe die Antwort aktualisiert. Sollte jetzt auch in FF funktionieren. Das Problem war, dass FF möchte, dass das Verknüpfungselement an das DOM angehängt wird. Dies ist in Chrome oder Safari nicht obligatorisch.
Messerbill

2
Es ist sinnvoll, am Ende URL.revokeObjectURL aufzurufen, um einen Speicherverlust zu vermeiden.
Andrew Simontsev

@ AndrewSimontsev Toller Tipp, danke für die Eingabe! Ich habe die Antwort bearbeitet und mich wissen lassen, ob sie auf diese Weise korrekt ist. Getestet auf meinem Code und scheint in Ordnung zu sein!
Jbarradas

1
Ich bin nicht der Meinung, dass die Verwendung einer Bibliothek eine bessere Antwort ist :). Die Verwendung externer Bibliotheken ist nicht immer eine Option, und wenn dies der Fall ist, ist es einfach, Bibliotheken zu finden. Es ist keine Antwort wert, weshalb Ihre Antwort wahrscheinlich mehr Stimmen als die akzeptierte Antwort hat, obwohl die akzeptierte Antwort 2 Jahre älter ist.
aggregate1166877

44

Ich löse dieses Problem vorübergehend mit download.js und blob.

let download = require('./download.min');

...

function downloadFile(token, fileId) {
  let url = `https://www.googleapis.com/drive/v2/files/${fileId}?alt=media`;
  return fetch(url, {
    method: 'GET',
    headers: {
      'Authorization': token
    }
  }).then(function(resp) {
    return resp.blob();
  }).then(function(blob) {
    download(blob);
  });
}

Es funktioniert für kleine Dateien, aber möglicherweise nicht für große Dateien. Ich denke ich sollte Stream mehr graben .


8
Denken Sie daran, dass die aktuelle Blob-Größenbeschränkung für Browser bei etwa 500 MB liegt
Tarikakyol

Schön, nur eines: Wäre es möglich, den Dateinamen aus der Serverantwort abzurufen, damit der Benutzer ihn mit seinem richtigen Namen herunterladen kann?
Phate

Die einzige Lösung, die ich für dieses Problem gefunden habe. Vielen Dank!
caravana_942

39

Dies ist kürzer und effizienter, keine Bibliotheken rufen nur API ab

const url ='http://sample.example.file.doc'
const authHeader ="Bearer 6Q************" 

const options = {
  headers: {
    Authorization: authHeader
  }
};
 fetch(url, options)
  .then( res => res.blob() )
  .then( blob => {
    var file = window.URL.createObjectURL(blob);
    window.location.assign(file);
  });


4
Gibt es eine Möglichkeit, den Dateinamen festzulegen?
Anton

2
Ja, Sie können Content-Disposition zu den Kopfzeilen hinzufügen. Hier finden Sie die Dokumentation developer.mozilla.org/en-US/docs/Web/HTTP/Headers/…
Lucas Matos

Dies ist schön.
Ryanpcmcquen

@LucasMatos Ich habe dem Optionsobjekt den Header "Content-Disposition" hinzugefügt und erhalte den richtigen Header für meine Datei, wenn ich sie auf der Registerkarte "Netzwerk" überprüfe. Dann wird der Blob erstellt und der Name wird weggeworfen, sodass ich einen generierten Zufall erhalte Name. Wissen Sie, wie Sie den Namen an blob mit Ihrer Lösung übergeben können?
miran80


11

function download(dataurl, filename) {
  var a = document.createElement("a");
  a.href = dataurl;
  a.setAttribute("download", filename);
  a.click();
  return false;
}

download("data:text/html,HelloWorld!", "helloWorld.txt");

oder:

function download(url, filename) {
fetch(url).then(function(t) {
    return t.blob().then((b)=>{
        var a = document.createElement("a");
        a.href = URL.createObjectURL(b);
        a.setAttribute("download", filename);
        a.click();
    }
    );
});
}

download("https://get.geojs.io/v1/ip/geo.json","geoip.json")
download("data:text/html,HelloWorld!", "helloWorld.txt");


8

Dowloadjs verwenden. Dadurch wird der Dateiname aus dem Header analysiert.

fetch("yourURL", {
    method: "POST",
    body: JSON.stringify(search),
    headers: {
        "Content-Type": "application/json; charset=utf-8"
    }
    })
    .then(response => {
        if (response.status === 200) {
            filename = response.headers.get("content-disposition");
            filename = filename.match(/(?<=")(?:\\.|[^"\\])*(?=")/)[0];
            return response.blob();
        } else {
        return;
        }
    })
    .then(body => {
        download(body, filename, "application/octet-stream");
    });
};

Das hat meistens funktioniert, aber ich habe stattdessen den regulären Ausdruck aus dieser anderen Antwort verwendet. Also ...fileName = fileName.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/)[1] ? fileName.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/)[1] : fileName;
thargenediad

1
Firefox unterstützt nicht filename.match(), ich habe diesen Teil ersetzt durch : filename = response.headers.get("content-disposition").split(";")[1].split('"')[1];. Außerdem muss der Server den Header deklarieren Access-Control-Expose-Headers: Content-Disposition, damit der Browser den content-dispositionHeader lesen kann .
Aizquier

3

Hier ist ein Beispiel für die Verwendung von Node-Fetch für alle, die dies finden.

reportRunner({url, params = {}}) {
    let urlWithParams = `${url}?`
    Object.keys(params).forEach((key) => urlWithParams += `&${key}=${params[key]}`)
    return fetch(urlWithParams)
        .then(async res => ({
            filename: res.headers.get('content-disposition').split('filename=')[1],
            blob: await res.blob()
        }))
        .catch(this.handleError)
}

Wo speichert es das? @ Michael Hobbs
FabricioG

Die Funktion gibt ein Objekt {Dateiname, Blob} zurück. Sie können fs.writefile verwenden, um die Datei auf der Festplatte zu speichern.
Michael Hobbs

1

Ich habe window.fetch ausprobiert, aber das wurde mit meiner REACT-App kompliziert

Jetzt ändere ich einfach window.location.href und füge Abfrageparameter wie das jsonwebtokenund hinzu other stuff.


///==== client side code =====
var url = new URL(`http://${process.env.REACT_APP_URL}/api/mix-sheets/list`);
url.searchParams.append("interval",data.interval);
url.searchParams.append("jwt",token)

window.location.href=url;

// ===== server side code =====

// on the server i set the content disposition to a file
var list = encodeToCsv(dataToEncode);
res.set({"Content-Disposition":`attachment; filename=\"FileName.csv\"`});
res.status(200).send(list)

Die Endergebnisse sind tatsächlich ziemlich gut, das Fenster stellt eine Anfrage und lädt die Datei herunter und der Ereignisschalter verschiebt die Seite nicht weg, als wäre der window.location.hrefAnruf wie ein zurückhaltender fetch()Anruf.


0

Wie bei einigen anderen Antworten können Sie auf jeden Fall window.fetch und download.js verwenden , um eine Datei herunterzuladen. Die Verwendung von window.fetch mit blob unterliegt jedoch den vom Browser auferlegten Speicherbeschränkungen, und die Kompatibilitätsbeschränkungen für download.js gelten ebenfalls .

Wenn Sie eine große Datei herunterladen müssen, möchten Sie sie nicht im Speicher der Client-Seite ablegen, um den Browser zu belasten, oder? Stattdessen möchten Sie es wahrscheinlich lieber über einen Stream herunterladen. In einem solchen Fall ist die Verwendung eines HTML-Links zum Herunterladen einer Datei eine der besten / einfachsten Möglichkeiten, insbesondere zum Herunterladen großer Dateien über einen Stream.

Erster Schritt: Erstellen und formatieren Sie ein Linkelement

Sie können den Link unsichtbar, aber dennoch umsetzbar machen.

HTML:

<a href="#" class="download-link" download>Download</a>

CSS:

.download-link {
  position: absolute;
  top: -9999px;
  left: -9999px;
  opacity: 0;
}

Schritt 2: Legen Sie den hrefLink fest und lösen Sie das clickEreignis aus

JavaScript

let url = `https://www.googleapis.com/drive/v2/files/${fileId}?alt=media`;

const downloadLink = document.querySelector('.download-link')
downloadLink.href = url + '&ts=' + new Date().getTime() // Prevent cache
downloadLink.click()

Hinweis : Sie können das Verknüpfungselement bei Bedarf dynamisch generieren.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.