Télécharger l'objet JSON en tant que fichier à partir du navigateur

144

J'ai le code suivant pour permettre aux utilisateurs de télécharger des chaînes de données dans un fichier csv.

exportData = 'data:text/csv;charset=utf-8,';
exportData += 'some csv strings';
encodedUri = encodeURI(exportData);
newWindow = window.open(encodedUri);

Cela fonctionne très bien que si le client exécute le code, il génère une page vierge et commence à télécharger les données dans le fichier csv.

J'ai donc essayé de le faire avec un objet JSON comme

exportData = 'data:text/json;charset=utf-8,';
exportData += escape(JSON.stringify(jsonObject));
encodedUri = encodeURI(exportData);
newWindow = window.open(encodedUri);

Mais je ne vois qu'une page avec les données JSON affichées dessus, sans la télécharger.

J'ai fait quelques recherches et celui-ci prétend fonctionner mais je ne vois aucune différence dans mon code.

Est-ce que je manque quelque chose dans mon code?

Merci d'avoir lu ma question :)

Eugène Yu
la source

Réponses:

257

Voici comment je l'ai résolu pour mon application:

HTML: <a id="downloadAnchorElem" style="display:none"></a>

JS (pur JS, pas jQuery ici):

var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(storageObj));
var dlAnchorElem = document.getElementById('downloadAnchorElem');
dlAnchorElem.setAttribute("href",     dataStr     );
dlAnchorElem.setAttribute("download", "scene.json");
dlAnchorElem.click();

Dans ce cas, storageObjest l'objet js que vous souhaitez stocker, et "scene.json" est juste un exemple de nom pour le fichier résultant.

Cette approche présente les avantages suivants par rapport aux autres propositions:

  • Aucun élément HTML ne doit être cliqué
  • Le résultat sera nommé comme vous le souhaitez
  • pas besoin de jQuery

J'avais besoin de ce comportement sans clic explicite car je veux déclencher le téléchargement automatiquement à un moment donné à partir de js.

Solution JS (aucun HTML requis):

  function downloadObjectAsJson(exportObj, exportName){
    var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(exportObj));
    var downloadAnchorNode = document.createElement('a');
    downloadAnchorNode.setAttribute("href",     dataStr);
    downloadAnchorNode.setAttribute("download", exportName + ".json");
    document.body.appendChild(downloadAnchorNode); // required for firefox
    downloadAnchorNode.click();
    downloadAnchorNode.remove();
  }
Volzotan
la source
3
C'est la seule solution qui fonctionnera pour plus de = ~ 2000 caractères de données. Parce que vous avez ajouté des données:
rjurney
1
Quelqu'un peut-il m'indiquer une spécification ou une page MDN qui explique plus en détail comment fonctionne tout ce type de données pré-ajouté, c'est-à-dire. "données: texte / json; charset = utf-8"? J'utilise ceci, mais cela semble magique, ce serait bien de lire les détails, mais je ne sais même pas comment chercher sur Google.
sidewinderguy
1
Cependant, cela ne fonctionne pas dans IE lorsque vous devez télécharger un gros fichier de plus de 2000 caractères.
user388969
12
Cela ne fonctionnera pas sans limites, cependant. Vous ne pouvez télécharger qu'environ 1 Mo de données. Par exemple, var storageObj = []; for (var i=0; i<1000000; ++i) storageObj.push('aaa');donne «Échec du téléchargement - Erreur réseau» dans Chrome 61
oseiskar
1
@YASHDAVE utiliser à la JSON.stringify(exportObj, null, 2)place
TryingToImprove
41

J'ai trouvé une réponse.

var obj = {a: 123, b: "4 5 6"};
var data = "text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(obj));

$('<a href="data:' + data + '" download="data.json">download JSON</a>').appendTo('#container');

semble bien fonctionner pour moi.

** Tout le mérite revient à @ cowboy-ben-alman, qui est l'auteur du code ci-dessus **

Eugène Yu
la source
@Cybear pouvez-vous m'expliquer la troisième ligne?
Rahul Khatri
Vous voudrez ajouter des données: à votre URL, sinon l'utilisateur est susceptible d'atteindre une limite de 2000 caractères dans de nombreux navigateurs.
rjurney
Hé, je sais que c'est une vieille réponse mais, sachez que cette solution ne fonctionne pas sur IE (tous) IE ne connaît pas la référence d'attribut de téléchargement - lien
Ayalon Grinfeld
25

Ce serait une version pure de JS (adaptée de cowboy's):

var obj = {a: 123, b: "4 5 6"};
var data = "text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(obj));

var a = document.createElement('a');
a.href = 'data:' + data;
a.download = 'data.json';
a.innerHTML = 'download JSON';

var container = document.getElementById('container');
container.appendChild(a);

http://jsfiddle.net/sz76c083/1

jbcdefg
la source
3
Merci pour la réponse même après 2 ans que j'ai posé cette question! Je préfère JS pur à la syntaxe jQuery.
Eugene Yu
Vous voudrez ajouter des données: à votre URL, sinon l'utilisateur est susceptible d'atteindre une limite de 2000 caractères dans de nombreux navigateurs.
rjurney
comment puis-je faire démarrer ce téléchargement lors du chargement de la page. chaque fois qu'un utilisateur navigue sur l'url de la page, il reçoit une invite de téléchargement
user388969
1
Le test d'aujourd'hui dans Edge: data.json n'a pas pu être téléchargé.
Sarah Trees
25

Vous pouvez essayer d'utiliser:

Pas besoin de traiter du tout les éléments HTML.

var data = {
    key: 'value'
};
var fileName = 'myData.json';

// Create a blob of the data
var fileToSave = new Blob([JSON.stringify(data)], {
    type: 'application/json',
    name: fileName
});

// Save the file
saveAs(fileToSave, fileName);

Si vous voulez bien imprimer le JSON, par cette réponse , vous pouvez utiliser:

JSON.stringify(data,undefined,2)
Gautham
la source
2
saveAs () provient de FileSaver.js - github.com/eligrey/FileSaver.js
Gautham
1
"Pas besoin de traiter des éléments HTML du tout" ... et la lecture du code source de filesaver.js ... il fait exactement cela lol
Guerre
1
Ouais. Je voulais dire, pas besoin de traiter directement les éléments HTML.
Gautham
3
C'est la meilleure réponse car elle n'a pas de limite de taille de 1 Mo et utilise une bibliothèque au lieu de hacks personnalisés
oseiskar
voté pour FileSaver réf. fonctionne parfaitement.
Noone
15

Ce qui suit a fonctionné pour moi:

/* function to save JSON to file from browser
* adapted from http://bgrins.github.io/devtools-snippets/#console-save
* @param {Object} data -- json object to save
* @param {String} file -- file name to save to 
*/
function saveJSON(data, filename){

    if(!data) {
        console.error('No data')
        return;
    }

    if(!filename) filename = 'console.json'

    if(typeof data === "object"){
        data = JSON.stringify(data, undefined, 4)
    }

    var blob = new Blob([data], {type: 'text/json'}),
        e    = document.createEvent('MouseEvents'),
        a    = document.createElement('a')

    a.download = filename
    a.href = window.URL.createObjectURL(blob)
    a.dataset.downloadurl =  ['text/json', a.download, a.href].join(':')
    e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null)
    a.dispatchEvent(e)
}

et puis l'appeler comme ça

saveJSON(myJsonObject, "saved_data.json");
maia
la source
3
Bien que ce soit une excellente réponse, il initMouseEvent()s'agit d'un standard Web obsolète et ne devrait plus être utilisé. Utilisez plutôt l' new MouseEvent()interface. C'est juste un petit refactor.
morkro
10

J'ai récemment dû créer un bouton qui téléchargerait un fichier json de toutes les valeurs d'une grande forme. J'en avais besoin pour travailler avec IE / Edge / Chrome. C'est ce que j'ai fait:

function download(text, name, type)
    {
        var file = new Blob([text], {type: type});
        var isIE = /*@cc_on!@*/false || !!document.documentMode;
        if (isIE)
        {
            window.navigator.msSaveOrOpenBlob(file, name);
        }
        else
        {
            var a = document.createElement('a');
            a.href = URL.createObjectURL(file);
            a.download = name;
            a.click();
        }
     }

download(jsonData, 'Form_Data_.json','application/json');

Il y avait un problème avec le nom de fichier et l'extension dans Edge, mais au moment de l'écriture, cela semblait être un bogue avec Edge qui devait être corrigé.

J'espère que cela aide quelqu'un

Brad
la source
Cela a fonctionné sur Chrome, mais ne semble pas fonctionner sur Firefox ... veuillez aider! codepen.io/anon/pen/ePYPJb
Urmil Parikh
1
Belle fonction, ajoutez document.body.appendChild(a); a.style.display = 'none';pour la faire fonctionner dans Firefox.
Fabian von Ellerts
5

Solution simple et propre pour ceux qui ne ciblent que les navigateurs modernes:

function downloadTextFile(text, name) {
  const a = document.createElement('a');
  const type = name.split(".").pop();
  a.href = URL.createObjectURL( new Blob([text], { type:`text/${type === "txt" ? "plain" : type}` }) );
  a.download = name;
  a.click();
}

downloadTextFile(JSON.stringify(myObj), 'myObj.json');
utilisateur993683
la source
Merci, un seul qui ne m'a pas donné de problèmes de taille
Roelant
4

La downloadpropriété des liens est nouvelle et n'est pas prise en charge dans Internet Explorer (voir le tableau de compatibilité ici ). Pour une solution multi-navigateurs à ce problème, je jetterais un œil à FileSaver.js

robertjd
la source
2

Réagissez : ajoutez ceci où vous voulez dans votre méthode de rendu.

• Objet en état :

<a
  className="pull-right btn btn-primary"
  style={{ margin: 10 }}
  href={`data:text/json;charset=utf-8,${encodeURIComponent(
  JSON.stringify(this.state.objectToDownload)
  )}`}
  download="data.json"
>
  DOWNLOAD DATA AS JSON
</a>

• Objet dans les accessoires :

<a
  className="pull-right btn btn-primary"
  style={{ margin: 10 }}
  href={`data:text/json;charset=utf-8,${encodeURIComponent(
  JSON.stringify(this.props.objectToDownload)
  )}`}
  download="data.json"
>
  DOWNLOAD DATA AS JSON
</a>

className et style sont facultatifs, modifiez le style selon vos besoins.

Abido
la source
1

Essayez de définir un autre type MIME: exportData = 'data:application/octet-stream;charset=utf-8,';

Mais il peut y avoir des problèmes avec le nom de fichier dans la boîte de dialogue d'enregistrement.

minimuline
la source
Désolé de vous être revenu tard. J'ai essayé votre réponse et il télécharge le fichier mais contient des données erronées. :(
Eugene Yu
1
cela a fonctionné pour moi ... data = "data:application/octet-stream;charset=utf-8," + encodeURIComponent(JSON.stringify(data)); window.open(data); il télécharge simplement le fichier en tant que "téléchargement" mais les données que j'ai stringifiées puis encodées en uri sont comme il se doit.
Jason Wiener
0

Si vous préférez l'extrait de console, raser, au nom de fichier, vous pouvez le faire:

window.open(URL.createObjectURL(
    new Blob([JSON.stringify(JSON)], {
      type: 'application/binary'}
    )
))
zb '
la source