JavaScript-Blob-Dateiname ohne Link

188

Wie legen Sie den Namen einer Blob-Datei in JavaScript fest, wenn Sie das Herunterladen über window.location erzwingen?

function newFile(data) {
    var json = JSON.stringify(data);
    var blob = new Blob([json], {type: "octet/stream"});
    var url  = window.URL.createObjectURL(blob);
    window.location.assign(url);
}

Durch Ausführen des obigen Codes wird eine Datei sofort heruntergeladen, ohne dass eine Seitenaktualisierung erfolgt, die wie folgt aussieht:

bfefe410-8d9c-4883-86c5-d76c50a24a1d

Ich möchte stattdessen den Dateinamen als my-download.json festlegen .

Aschblau
quelle

Antworten:

311

Der einzige Weg, den ich kenne, ist der von FileSaver.js verwendete Trick :

  1. Erstellen Sie ein verstecktes <a>Tag.
  2. Legen Sie das hrefAttribut auf die URL des Blobs fest.
  3. Setzen Sie das downloadAttribut auf den Dateinamen.
  4. Klicken Sie auf das <a>Tag.

Hier ist ein vereinfachtes Beispiel ( jsfiddle ):

var saveData = (function () {
    var a = document.createElement("a");
    document.body.appendChild(a);
    a.style = "display: none";
    return function (data, fileName) {
        var json = JSON.stringify(data),
            blob = new Blob([json], {type: "octet/stream"}),
            url = window.URL.createObjectURL(blob);
        a.href = url;
        a.download = fileName;
        a.click();
        window.URL.revokeObjectURL(url);
    };
}());

var data = { x: 42, s: "hello, world", d: new Date() },
    fileName = "my-download.json";

saveData(data, fileName);

Ich habe dieses Beispiel geschrieben, um die Idee zu veranschaulichen. Verwenden Sie im Produktionscode stattdessen FileSaver.js.

Anmerkungen

  • Ältere Browser unterstützen das Attribut "Download" nicht, da es Teil von HTML5 ist.
  • Einige Dateiformate werden vom Browser als unsicher eingestuft und der Download schlägt fehl. Das Speichern von JSON-Dateien mit der Erweiterung txt funktioniert bei mir.
kol
quelle
2
@AshBlue Das Attribut "download" benötigt HTML5. Mein Code ist nur ein Beispiel. Sie können auch die Demoseite von FileSaver.js
kol
1
Interessanterweise schlägt der Download manchmal fehl, wenn Sie wiederholt versuchen, einen Text auf diese Weise herunterzuladen (indem Sie immer wieder auf die Schaltfläche Ausführen auf jsfiddle.net klicken).
Kol
2
Ich wollte nur erwähnen, dass diese Lösung nicht für Dateien mit Größen größer als ein bestimmter Schwellenwert funktioniert. zB-> 2 MB für Chrom. Diese Größe variiert von Browser zu Browser.
Manojadams
3
Dies funktioniert bei mir nicht, da ich die Datei in einem neuen Tab öffnen muss. Ich muss ein PDF in Chrome anzeigen, aber ich muss einen benutzerfreundlichen Namen in der URL-Symbolleiste anzeigen. Wenn der Benutzer über das Download-Symbol herunterladen möchte, muss ich denselben benutzerfreundlichen Namen in die Datei einfügen.
Adrian Paredes
1
Nur um hinzuzufügen, müssen Sie das a-Tag nicht wirklich am Körper
jenseits des Codes
50

Ich wollte nur die akzeptierte Antwort mit Unterstützung für Internet Explorer (jedenfalls die modernsten Versionen) erweitern und den Code mit jQuery aufräumen:

$(document).ready(function() {
    saveFile("Example.txt", "data:attachment/text", "Hello, world.");
});

function saveFile (name, type, data) {
    if (data !== null && navigator.msSaveBlob)
        return navigator.msSaveBlob(new Blob([data], { type: type }), name);
    var a = $("<a style='display: none;'/>");
    var url = window.URL.createObjectURL(new Blob([data], {type: type}));
    a.attr("href", url);
    a.attr("download", name);
    $("body").append(a);
    a[0].click();
    window.URL.revokeObjectURL(url);
    a.remove();
}

Hier ist ein Beispiel für Fiddle . Gute Fahrt .

Alexandru
quelle
Hat perfekt funktioniert.
N8allan
1
Ich habe die akzeptierte Lösung verwendet, aber bei Firefox hat es nicht funktioniert! Ich weiß immer noch nicht warum. Ihre Lösung hat in Firefox funktioniert. Vielen Dank.
Elahehab
27

Gleiches Prinzip wie die obigen Lösungen. Ich hatte jedoch Probleme mit Firefox 52.0 (32 Bit), bei denen große Dateien (> 40 MByte) an zufälligen Positionen abgeschnitten wurden. Das Neuplanen des Aufrufs von revokeObjectUrl () behebt dieses Problem.

function saveFile(blob, filename) {
  if (window.navigator.msSaveOrOpenBlob) {
    window.navigator.msSaveOrOpenBlob(blob, filename);
  } else {
    const a = document.createElement('a');
    document.body.appendChild(a);
    const url = window.URL.createObjectURL(blob);
    a.href = url;
    a.download = filename;
    a.click();
    setTimeout(() => {
      window.URL.revokeObjectURL(url);
      document.body.removeChild(a);
    }, 0)
  }
}

jsfiddle Beispiel

Kim Nyholm
quelle
1
Ich fand heraus, dass dieser setTimeout () - Hack MS Edge behebt, bei dem die Datei überhaupt nicht heruntergeladen werden konnte. Es muss jedoch nur der Aufruf von revokeObjectURL () verzögert werden.
Russell Phillips
Ich fand, dass das "if (window.navigator.msSaveOrOpenBlob)" der Trick für mich war
Jacques Olivier
22

Spät, aber da ich das gleiche Problem hatte, füge ich meine Lösung hinzu:

function newFile(data, fileName) {
    var json = JSON.stringify(data);
    //IE11 support
    if (window.navigator && window.navigator.msSaveOrOpenBlob) {
        let blob = new Blob([json], {type: "application/json"});
        window.navigator.msSaveOrOpenBlob(blob, fileName);
    } else {// other browsers
        let file = new File([json], fileName, {type: "application/json"});
        let exportUrl = URL.createObjectURL(file);
        window.location.assign(exportUrl);
        URL.revokeObjectURL(exportUrl);
    }
}
ben
quelle
5
Danke @ben. Das funktioniert gut. Keine dom-Elemente, nichts wie ein Klickereignis auslösen. Es funktioniert einfach fantastisch mit der richtigen Erweiterung. Der angegebene Dateiname wird jedoch nicht berücksichtigt, da "<object_url_id> .csv" anstelle von "<myfileName> .csv" heruntergeladen wird
Ram Babu S
3
Das Aufrufen revokeObjectURLnach location.assignfunktioniert in Firefox einwandfrei, unterbricht jedoch den Download in Chrome.
Fred
Beachten Sie, dass "Edge den Dateikonstruktor nicht unterstützt." Ref. caniuse.com/#feat=fileapi
user1477388
Dies sollte die richtige Antwort sein. Es macht keinen Sinn, nutzlose Objekte im DOM-Baum zu erstellen
Luiz Felipe
Jetzt schon seit dem
Luiz Felipe
6
saveFileOnUserDevice = function(file){ // content: blob, name: string
        if(navigator.msSaveBlob){ // For ie and Edge
            return navigator.msSaveBlob(file.content, file.name);
        }
        else{
            let link = document.createElement('a');
            link.href = window.URL.createObjectURL(file.content);
            link.download = file.name;
            document.body.appendChild(link);
            link.dispatchEvent(new MouseEvent('click', {bubbles: true, cancelable: true, view: window}));
            link.remove();
            window.URL.revokeObjectURL(link.href);
        }
    }
Jean-Philippe
quelle
Gibt es eine Möglichkeit, darin ein neues Fenster zu öffnen?
Enrique Altuna
Ich denke, Sie können anrufen, link.click()anstatt ein Mausereignis auszulösen.
Fred
2

Arbeitsbeispiel eines Download-Buttons zum Speichern eines Katzenfotos von einer URL als "cat.jpg":

HTML:

<button onclick="downloadUrl('https://i.imgur.com/AD3MbBi.jpg', 'cat.jpg')">Download</button>

JavaScript:

function downloadUrl(url, filename) {
  let xhr = new XMLHttpRequest();
  xhr.open("GET", url, true);
  xhr.responseType = "blob";
  xhr.onload = function(e) {
    if (this.status == 200) {
      const blob = this.response;
      const a = document.createElement("a");
      document.body.appendChild(a);
      const blobUrl = window.URL.createObjectURL(blob);
      a.href = blobUrl;
      a.download = filename;
      a.click();
      setTimeout(() => {
        window.URL.revokeObjectURL(blobUrl);
        document.body.removeChild(a);
      }, 0);
    }
  };
  xhr.send();
}
user1032613
quelle
1

window.location.assign hat bei mir nicht funktioniert. Es wird problemlos heruntergeladen, jedoch ohne Erweiterung für eine CSV-Datei auf der Windows-Plattform. Folgendes hat bei mir funktioniert.

    var blob = new Blob([csvString], { type: 'text/csv' });
    //window.location.assign(window.URL.createObjectURL(blob));
    var link = window.document.createElement('a');
    link.href = window.URL.createObjectURL(blob);
    // Construct filename dynamically and set to link.download
    link.download = link.href.split('/').pop() + '.' + extension; 
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
Sacky San
quelle
0

Das ist meine Lösung. Aus meiner Sicht kann man das nicht umgehen <a>.

function export2json() {
  const data = {
    a: '111',
    b: '222',
    c: '333'
  };
  const a = document.createElement("a");
  a.href = URL.createObjectURL(
    new Blob([JSON.stringify(data, null, 2)], {
      type: "application/json"
    })
  );
  a.setAttribute("download", "data.json");
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
}
<button onclick="export2json()">Export data to json file</button>

dabeng
quelle