Descargue el objeto JSON como un archivo desde el navegador

144

Tengo el siguiente código para permitir a los usuarios descargar cadenas de datos en el archivo csv.

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

Funciona bien que si el cliente ejecuta el código, genera una página en blanco y comienza a descargar los datos en un archivo csv.

Así que intenté hacer esto con un objeto JSON como

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

Pero solo veo una página con los datos JSON mostrados, no la descargo.

Investigué un poco y este dice que funciona, pero no veo ninguna diferencia en mi código.

¿Me falta algo en mi código?

Gracias por leer mi pregunta:)

Eugene Yu
fuente

Respuestas:

257

Así es como lo resolví para mi aplicación:

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

JS (JS puro, no jQuery aquí):

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

En este caso, storageObjes el objeto js que desea almacenar, y "scene.json" es solo un nombre de ejemplo para el archivo resultante.

Este enfoque tiene las siguientes ventajas sobre otras propuestas:

  • No se debe hacer clic en ningún elemento HTML
  • El resultado se nombrará como lo desee
  • no se necesita jQuery

Necesitaba este comportamiento sin hacer clic explícitamente, ya que quiero activar la descarga automáticamente en algún momento desde js.

Solución JS (no se requiere HTML):

  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
fuente
3
Esta es la única solución que funcionará para más de = ~ 2000 caracteres de datos. Debido a que agregó datos:
rjurney
1
¿Alguien puede señalarme una página de especificaciones o MDN que explique con más detalle cómo funciona todo este tipo de datos antepuestos? "data: text / json; charset = utf-8"? Estoy usando esto, pero parece mágico, sería genial leer los detalles, pero ni siquiera sé cómo buscarlo en Google.
sidewinderguy
1
Sin embargo, esto no funciona en IE cuando tienes que descargar un archivo grande con más de 2000 caracteres.
user388969
12
Sin embargo, esto no funcionará sin límites. Solo puede descargar aproximadamente 1 MB de datos. Por ejemplo, var storageObj = []; for (var i=0; i<1000000; ++i) storageObj.push('aaa');muestra "Error de descarga - Error de red" en Chrome 61
oseiskar
1
@YASHDAVE usar JSON.stringify(exportObj, null, 2)en su lugar
TryingToImprove
40

Encontré una respuesta.

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

Parece funcionar bien para mi.

** Todo el crédito va a @ cowboy-ben-alman, quien es el autor del código anterior **

Eugene Yu
fuente
@Cybear, ¿puedes explicarme la tercera línea?
Rahul Khatri
Deberá anteponer los datos: a su URL, de lo contrario es probable que el usuario alcance un límite de 2000 caracteres en muchos navegadores.
rjurney
Hola, sé que es una respuesta anterior, pero sé que esta solución no funciona en IE (todos ellos) IE no está familiarizado con la referencia de atributo de descarga - enlace
Ayalon Grinfeld
25

Esta sería una versión pura de JS (adaptada de cowboy):

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
fuente
3
¡Gracias por la respuesta incluso después de que hayan pasado 2 años desde que hice esta pregunta! Prefiero JS puro sobre la sintaxis jQuery.
Eugene Yu
Deberá anteponer los datos: a su URL, de lo contrario es probable que el usuario alcance un límite de 2000 caracteres en muchos navegadores.
rjurney 01 de
¿Cómo puedo hacer que esta descarga comience en la carga de la página? siempre que un usuario explora la página URL que recibe pronta para descargar
user388969
1
La prueba de hoy en Edge: data.json no se pudo descargar.
Sarah Trees el
24

Puedes intentar usar:

No es necesario tratar con ningún elemento 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 desea imprimir bastante el JSON, según esta respuesta , puede usar:

JSON.stringify(data,undefined,2)
Gautham
fuente
1
saveAs () es de FileSaver.js - github.com/eligrey/FileSaver.js
Gautham
1
"No es necesario tratar con ningún elemento HTML" ... y leer el código fuente de filesaver.js ... hace exactamente eso jajaja
War
1
Si. Quería decir que no es necesario tratar directamente con los elementos HTML.
Gautham
3
Esta es la mejor respuesta, ya que no tiene un límite de tamaño de 1 MB y utiliza una biblioteca en lugar de cortes personalizados
oseiskar
votado a favor para FileSaver ref. funcionando perfectamente
Nadie el
15

Lo siguiente funcionó para mí:

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

y luego llamarlo así

saveJSON(myJsonObject, "saved_data.json");
maia
fuente
3
Si bien esta es una gran respuesta, initMouseEvent()es un estándar web obsoleto y ya no debe usarse. En su lugar, use la new MouseEvent()interfaz. Sin embargo, es solo un refactor menor.
morkro
10

Recientemente tuve que crear un botón que descargaría un archivo json de todos los valores de una forma grande. Necesitaba esto para trabajar con IE / Edge / Chrome. Esto es lo que hice:

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

Hubo un problema con el nombre de archivo y la extensión en Edge, pero al momento de escribir esto parecía ser un error con Edge que se debe solucionar.

Espero que esto ayude a alguien

Puntilla
fuente
Esto funcionó en Chrome, pero no parece funcionar en Firefox ... ¡por favor ayuda! codepen.io/anon/pen/ePYPJb
Urmil Parikh
1
Buena función, agregue document.body.appendChild(a); a.style.display = 'none';para que funcione en Firefox.
Fabian von Ellerts
5

Solución simple y limpia para aquellos que solo se dirigen a los navegadores modernos:

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');
user993683
fuente
Gracias, solo uno que no me dio problemas de tamaño
Roelant
4

La downloadpropiedad de los enlaces es nueva y no es compatible con Internet Explorer (consulte la tabla de compatibilidad aquí ). Para una solución de navegador cruzado a este problema, echaría un vistazo a FileSaver.js

robertjd
fuente
2

Reaccionar : agrega esto donde quieras en tu método de renderizado.

• Objeto en estado :

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

• Objeto en accesorios :

<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 y style son opcionales, modifique el estilo según sus necesidades.

Abido
fuente
1

Intente configurar otro tipo MIME: exportData = 'data:application/octet-stream;charset=utf-8,';

Pero puede haber problemas con el nombre del archivo en el cuadro de diálogo Guardar.

minimulina
fuente
Perdón por volver tarde. Intenté su respuesta y descarga el archivo, pero contiene datos incorrectos. :(
Eugene Yu
1
esto funcionó para mí ... data = "data:application/octet-stream;charset=utf-8," + encodeURIComponent(JSON.stringify(data)); window.open(data); simplemente descarga el archivo como "descargar" pero los datos que encadené y luego codifiqué con uri son como deberían ser.
Jason Wiener
0

Si prefiere el fragmento de consola, el raser, que el nombre de archivo, puede hacer esto:

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