Nom de fichier blob JavaScript sans lien

189

Comment définir le nom d'un fichier blob en JavaScript lorsque vous forcez son téléchargement via window.location?

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

L'exécution du code ci-dessus télécharge un fichier instantanément sans actualisation de la page qui ressemble à:

bfefe410-8d9c-4883-86c5-d76c50a24a1d

Je veux plutôt définir le nom de fichier comme my-download.json .

Bleu cendre
la source

Réponses:

313

Le seul moyen dont je suis conscient est l'astuce utilisée par FileSaver.js :

  1. Créez une <a>balise masquée .
  2. Définissez son hrefattribut sur l'URL de l'objet blob.
  3. Définissez son downloadattribut sur le nom de fichier.
  4. Cliquez sur l' <a>étiquette.

Voici un exemple simplifié ( 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);

J'ai écrit cet exemple juste pour illustrer l'idée, dans le code de production, utilisez plutôt FileSaver.js.

Remarques

  • Les navigateurs plus anciens ne prennent pas en charge l'attribut "download", car il fait partie de HTML5.
  • Certains formats de fichiers sont considérés comme non sécurisés par le navigateur et le téléchargement échoue. L'enregistrement de fichiers JSON avec l'extension txt fonctionne pour moi.
kol
la source
2
@AshBlue L'attribut "download" nécessite HTML5. Mon code n'est qu'un exemple, vous pouvez également essayer la page de démonstration FileSaver.js
kol
1
Fait intéressant, si vous essayez à plusieurs reprises de télécharger un txt de cette façon (en appuyant sur le bouton Exécuter sur jsfiddle.net encore et encore), le téléchargement échoue parfois.
kol
2
Je voulais juste mentionner que cette solution ne fonctionnera pas pour les fichiers dont la taille est supérieure à un seuil particulier. par exemple -> 2 Mo pour chrome. Cette taille varie d'un navigateur à l'autre.
manojadams le
3
Cela ne fonctionne pas pour moi car je dois ouvrir le fichier dans un nouvel onglet. Je dois afficher un PDF dans Chrome, mais je dois afficher un nom convivial dans la barre d'outils URL, et si l'utilisateur souhaite télécharger via l'icône de téléchargement, je dois mettre le même nom convivial dans le fichier.
Adrian Paredes
1
Juste pour ajouter, vous n'avez pas besoin de monter la balise a sur le corps pour que cela fonctionne (essayé tout à l'heure dans Chrome)
Beyond-code
52

Je voulais juste développer la réponse acceptée avec la prise en charge d'Internet Explorer (la plupart des versions modernes, de toute façon), et ranger le code en utilisant jQuery:

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

Voici un exemple de violon . Godspeed .

Alexandru
la source
A parfaitement fonctionné.
N8allan
1
J'ai utilisé la solution acceptée mais cela n'a pas fonctionné chez Firefox! Je ne sais toujours pas pourquoi. Votre solution a fonctionné dans Firefox. Merci.
elahehab
@elahehab Mes solutions fonctionnent toujours;)
Alexandru
27

Même principe que les solutions ci-dessus. Mais j'ai eu des problèmes avec Firefox 52.0 (32 bits) où de gros fichiers (> 40 Mo) sont tronqués à des positions aléatoires. La reprogrammation de l'appel de revokeObjectUrl () résout ce problème.

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

exemple jsfiddle

Kim Nyholm
la source
1
J'ai trouvé que ce hack setTimeout () corrige MS Edge, où le fichier ne se télécharge pas du tout. Cependant, seul l'appel à revokeObjectURL () doit être retardé.
Russell Phillips
J'ai trouvé que le "if (window.navigator.msSaveOrOpenBlob)" est ce qui a fait l'affaire pour moi
Jacques Olivier
23

En retard, mais comme j'ai eu le même problème j'ajoute ma solution:

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
la source
5
Merci @ben. Cela fonctionne bien. Aucun élément dom, rien de tel que de déclencher un événement de clic. Cela fonctionne tout simplement génial avec une extension appropriée. Mais le nom de fichier donné n'est pas pris en compte, le téléchargement de "<object_url_id> .csv" au lieu de "<myfileName> .csv"
Ram Babu S
3
L'appel revokeObjectURLaprès location.assignfonctionne bien dans Firefox, mais interrompt le téléchargement sur Chrome.
Fred
Notez que «Edge ne prend pas en charge le constructeur de fichiers». Réf. caniuse.com/#feat=fileapi
user1477388
Cela devrait être la bonne réponse. Inutile de créer des objets inutiles dans l'arbre DOM
Luiz Felipe
Maintenant c'est le cas, depuis janvier 2020
Luiz Felipe le
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
la source
y a-t-il un moyen d'y ouvrir une nouvelle fenêtre?
Enrique Altuna
Je pense que vous pouvez appeler link.click()au lieu d'envoyer un événement de souris.
Fred
2

Exemple fonctionnel d'un bouton de téléchargement, pour enregistrer une photo de chat à partir d'une URL sous le nom "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
la source
1

window.location.assign n'a pas fonctionné pour moi. il télécharge bien mais télécharge sans extension pour un fichier CSV sur la plate-forme Windows. Ce qui suit a fonctionné pour moi.

    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
la source
0

C'est ma solution. De mon point de vue, vous ne pouvez pas contourner le <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
la source