Faça o download do objeto JSON como um arquivo do navegador

144

Eu tenho o código a seguir para permitir que os usuários baixem cadeias de dados no arquivo csv.

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

Funciona muito bem que, se o cliente executa o código, ele gera uma página em branco e começa a baixar os dados no arquivo csv.

Então, eu tentei fazer isso com o objeto JSON como

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

Mas vejo apenas uma página com os dados JSON exibidos nela, não fazendo o download.

Passei por algumas pesquisas e esta afirma funcionar, mas não vejo nenhuma diferença no meu código.

Estou faltando alguma coisa no meu código?

Obrigado por ler minha pergunta :)

Eugene Yu
fonte

Respostas:

257

Foi assim que resolvi para o meu aplicativo:

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

JS (JS puro, não jQuery aqui):

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

Nesse caso, storageObjé o objeto js que você deseja armazenar e "scene.json" é apenas um nome de exemplo para o arquivo resultante.

Essa abordagem tem as seguintes vantagens sobre outras propostas:

  • Nenhum elemento HTML precisa ser clicado
  • O resultado será nomeado como você deseja
  • não é necessário jQuery

Eu precisava desse comportamento sem clicar explicitamente, pois desejo acionar o download automaticamente em algum momento do js.

Solução JS (não é necessário 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
fonte
3
Esta é a única solução que funcionará para mais de = ~ 2000 caracteres de dados. Porque você prefixado dados:
rjurney
1
Alguém pode me apontar para uma página de especificação ou MDN que explique em mais detalhes como funciona todo esse tipo de dados anexados? "dados: texto / json; charset = utf-8"? Estou usando isso, mas parece mágico, seria ótimo ler sobre os detalhes, mas eu nem sei como pesquisar no google.
sidewinderguy
1
Porém, isso não funciona no IE quando você precisa fazer o download de um arquivo grande com mais de 2000 caracteres.
user388969
12
Isso não funcionará sem limites, no entanto. Você só pode baixar cerca de 1 MB de dados. Por exemplo, var storageObj = []; for (var i=0; i<1000000; ++i) storageObj.push('aaa');fornece "Download com falha - erro de rede" no Chrome 61
oseiskar
1
@YASHDAVE use JSON.stringify(exportObj, null, 2)vez
TryingToImprove
40

Encontrei uma resposta.

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 bem para mim.

** Todo o crédito é para @ cowboy-ben-alman, que é o autor do código acima **

Eugene Yu
fonte
@ Cybear, você pode me explicar a terceira linha?
Rahul Khatri
Você deseja anexar dados anteriormente: ao seu URL, caso contrário, o usuário provavelmente atingirá um limite de 2.000 caracteres em muitos navegadores.
rjurney
Ei, eu sei que é uma resposta antiga, mas sei que esta solução não funciona no IE (todos eles) O IE não está familiarizado com a referência de atributo de download - link
Ayalon Grinfeld
25

Esta seria uma versão JS pura (adaptada do 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
fonte
3
Obrigado pela resposta, mesmo depois de dois anos desde que fiz essa pergunta! Eu prefiro JS puro sobre sintaxe jQuery.
Eugene Yu
Você deseja anexar dados anteriormente: ao seu URL, caso contrário, o usuário provavelmente atingirá um limite de 2.000 caracteres em muitos navegadores.
rjurney
como posso fazer esse download iniciar no carregamento da página. sempre que um usuário procura o URL da página que ele recebe pronta para download
user388969
1
O teste de hoje no Edge: data.json não pôde ser baixado.
Sarah Trees
24

Você pode tentar usar:

  • as APIs nativas do JavaScript construtor Blob e
  • o método FileSaver.js saveAs()

Não há necessidade de lidar com nenhum 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);

Se você quiser imprimir bastante o JSON, de acordo com esta resposta , poderá usar:

JSON.stringify(data,undefined,2)
Gautham
fonte
1
saveAs () é de FileSaver.js - github.com/eligrey/FileSaver.js
Gautham
1
"Não há necessidade de lidar com nenhum elemento HTML" ... e lendo o código fonte do filesaver.js ... ele faz exatamente isso lol
War
1
Sim. Eu quis dizer, não há necessidade de lidar diretamente com os elementos HTML.
Gautham 25/09
3
Esta é a melhor resposta, uma vez que não tem um limite de tamanho de 1 MB e usa uma biblioteca em vez de hacks personalizados
oseiskar
votado positivamente para o FileSaver ref. funcionando perfeitamente.
Noone
15

O seguinte funcionou para mim:

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

e então chamá-lo assim

saveJSON(myJsonObject, "saved_data.json");
maia
fonte
3
Embora essa seja uma ótima resposta, initMouseEvent()é um padrão da Web obsoleto e não deve mais ser usado. Em vez disso, use a new MouseEvent()interface. É apenas um refator menor, no entanto.
Morkro 28/05
10

Recentemente, tive que criar um botão que baixasse um arquivo json de todos os valores de um formulário grande. Eu precisava disso para trabalhar com o IE / Edge / Chrome. Isto é o que eu fiz:

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

Houve um problema com o nome do arquivo e a extensão no Edge, mas no momento da redação deste documento parecia haver um bug no Edge que deveria ser corrigido.

Espero que isso ajude alguém

Brad
fonte
Isso funcionou no Chrome, mas parece não funcionar no Firefox ... por favor, ajude! codepen.io/anon/pen/ePYPJb
Urmil Parikh
1
Função agradável, adicione document.body.appendChild(a); a.style.display = 'none';para fazê-lo funcionar no Firefox.
Fabian von Ellerts
5

Solução simples e limpa para aqueles que segmentam apenas 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
fonte
Obrigado, único que não me deu problemas de tamanho
Roelant
4

A downloadpropriedade dos links é nova e não é suportada no Internet Explorer (consulte a tabela de compatibilidade aqui ). Para uma solução de navegador cruzado para esse problema, daria uma olhada no FileSaver.js

robertjd
fonte
2

Reagir : adicione isso onde desejar no seu método de renderização.

• Objeto no 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 em adereços :

<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 e style são opcionais, modifique o estilo de acordo com suas necessidades.

Abido
fonte
1

Tente definir outro tipo MIME: exportData = 'data:application/octet-stream;charset=utf-8,';

Mas pode haver problemas com o nome do arquivo na caixa de diálogo Salvar.

minimulina
fonte
Desculpe por voltar tarde. Eu tentei a sua resposta e ele faz arquivo de download, mas contém dados errados nele .. :(
Eugene Yu
1
isso funcionou para mim ... data = "data:application/octet-stream;charset=utf-8," + encodeURIComponent(JSON.stringify(data)); window.open(data); ele simplesmente baixa o arquivo como "download", mas os dados que eu codifiquei e depois codi-uri são como deveriam.
21413 Jason Wiener
0

Se você preferir o snippet do console, raser, do que o nome do arquivo, faça o seguinte:

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