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

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

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


@ 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


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)

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

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


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

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


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.


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.


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'}
    )
))
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.