Pobierz obiekt JSON jako plik z przeglądarki

144

Mam następujący kod, aby umożliwić użytkownikom pobieranie ciągów danych w pliku csv.

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

Działa dobrze, jeśli klient uruchamia kod, generuje pustą stronę i rozpoczyna pobieranie danych w pliku csv.

Więc próbowałem to zrobić z obiektem JSON, takim jak

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

Ale widzę tylko stronę z wyświetlonymi danymi JSON, nie pobieram jej.

Przeszedłem przez kilka badań i ten twierdzi, że działa, ale nie widzę żadnej różnicy w moim kodzie.

Czy coś mi brakuje w kodzie?

Dziękuję za przeczytanie mojego pytania :)

Eugene Yu
źródło

Odpowiedzi:

257

Oto jak rozwiązałem to dla mojej aplikacji:

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

JS (czysty JS, nie jQuery tutaj):

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

W tym przypadku storageObjjest to obiekt js, który chcesz przechowywać, a „scena.json” to tylko przykładowa nazwa pliku wynikowego.

Takie podejście ma następujące zalety w porównaniu z innymi proponowanymi:

  • Żaden element HTML nie musi być klikany
  • Wynik zostanie nazwany tak, jak chcesz
  • nie jest potrzebne jQuery

Potrzebowałem tego zachowania bez wyraźnego klikania, ponieważ chcę automatycznie uruchomić pobieranie w pewnym momencie z js.

Rozwiązanie JS (bez 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
źródło
3
Jest to jedyne rozwiązanie, które będzie działać dla danych powyżej = ~ 2000 znaków. Ponieważ dodałeś dane:
rjurney
1
Czy ktoś może wskazać mi specyfikację lub stronę MDN, która wyjaśnia bardziej szczegółowo, jak działa ta cała rzecz z dołączonym typem danych, tj. "data: text / json; charset = utf-8"? Używam tego, ale czuję się jak magia, wspaniale byłoby przeczytać szczegóły, ale nawet nie wiem, jak to znaleźć w Google.
sidewinderguy
1
Nie działa to jednak w IE, gdy musisz pobrać duży plik zawierający ponad 2000 znaków.
user388969
12
Nie będzie to jednak działać bez ograniczeń. Możesz pobrać tylko około 1 MB danych. Na przykład var storageObj = []; for (var i=0; i<1000000; ++i) storageObj.push('aaa');w przeglądarce Chrome 61
oseiskar
1
Zamiast JSON.stringify(exportObj, null, 2)tego użyj @YASHDAVE
TryingToImprove
40

Znalazłem odpowiedź.

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

wydaje mi się, że działa dobrze dla mnie.

** Wszystkie zasługi dla @ cowboy-ben-alman, który jest autorem powyższego kodu **

Eugene Yu
źródło
@ Cybear, czy możesz mi wyjaśnić trzecią linijkę?
Rahul Khatri
Będziesz chciał dołączyć dane: do swojego adresu URL, w przeciwnym razie użytkownik prawdopodobnie przekroczy limit 2000 znaków w wielu przeglądarkach.
rjurney
Hej, wiem, że to stara odpowiedź, ale wiem, że to rozwiązanie nie działa na IE (wszystkie z nich) IE nie zna odniesienia do atrybutu pobierania - link
Ayalon Grinfeld
25

To byłaby czysta wersja JS (zaadaptowana z kowbojskiego):

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
źródło
3
Dziękuję za odpowiedź nawet po 2 latach, odkąd zadałem to pytanie! Wolę czysty JS od składni jQuery.
Eugene Yu
Będziesz chciał dołączyć dane: do swojego adresu URL, w przeciwnym razie użytkownik prawdopodobnie przekroczy limit 2000 znaków w wielu przeglądarkach.
rjurney
jak mogę rozpocząć pobieranie po załadowaniu strony. za każdym razem, gdy użytkownik przegląda adres URL strony, otrzymuje monit o pobranie
user388969
1
Nie można pobrać dzisiejszego testu w Edge: data.json.
Sarah Trees
25

Możesz spróbować użyć:

  • natywny konstruktor Blob interfejsu API JavaScript i
  • FileSaver.js saveAs() Sposób

Nie musisz w ogóle zajmować się żadnymi elementami 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);

Jeśli chcesz ładnie wydrukować JSON, zgodnie z tą odpowiedzią , możesz użyć:

JSON.stringify(data,undefined,2)
Gautham
źródło
1
saveAs () pochodzi z FileSaver.js - github.com/eligrey/FileSaver.js
Gautham
1
„W ogóle nie trzeba zajmować się żadnymi elementami HTML”… i czytając kod źródłowy filesaver.js… robi dokładnie to, co lol
War,
1
Tak. Chciałem powiedzieć, że nie ma potrzeby zajmowania się bezpośrednio elementami HTML.
Gautham
3
To najlepsza odpowiedź, ponieważ nie ma limitu rozmiaru 1 MB i używa biblioteki zamiast niestandardowych hacków
oseiskar
głosował w górę na FileSaver ref. działa idealnie.
Noone
15

Pracowały dla mnie:

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

a potem tak to nazwać

saveJSON(myJsonObject, "saved_data.json");
maia
źródło
3
Chociaż jest to świetna odpowiedź, initMouseEvent()jest to przestarzały standard sieciowy i nie powinien być już używany. Zamiast tego użyj new MouseEvent()interfejsu. To tylko drobny refaktor.
morkro
10

Niedawno musiałem utworzyć przycisk, który pobierałby plik json ze wszystkimi wartościami dużego formularza. Potrzebowałem tego do pracy z IE / Edge / Chrome. Oto co zrobiłem:

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

Wystąpił jeden problem z nazwą pliku i rozszerzeniem w Edge, ale w momencie pisania wydawało się, że jest to błąd w Edge, który ma zostać naprawiony.

Mam nadzieję, że to komuś pomoże

Ćwiek
źródło
To działało w przeglądarce Chrome, ale wygląda na to, że nie działa w przeglądarce Firefox ... proszę o pomoc! codepen.io/anon/pen/ePYPJb
Urmil Parikh
1
Niezła funkcja, dodaj, document.body.appendChild(a); a.style.display = 'none';aby działała w przeglądarce Firefox.
Fabian von Ellerts
5

Proste, czyste rozwiązanie dla tych, którzy kierują reklamy tylko na nowoczesne przeglądarki:

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
źródło
Dzięki, tylko jeden, który nie spowodował problemów z rozmiarem
Roelant
4

downloadNieruchomość linków jest nowy i nie jest obsługiwany w przeglądarce Internet Explorer (patrz tabela kompatybilności tutaj ). Aby znaleźć rozwiązanie tego problemu w różnych przeglądarkach, przyjrzyj się plikowi FileSaver.js

robertjd
źródło
2

Reaguj : dodaj to w dowolnym miejscu w metodzie renderowania.

• Obiekt w stanie :

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

• Obiekt w rekwizytach :

<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 i style są opcjonalne, zmodyfikuj styl zgodnie ze swoimi potrzebami.

Abido
źródło
1

Spróbuj ustawić inny typ MIME: exportData = 'data:application/octet-stream;charset=utf-8,';

Ale mogą wystąpić problemy z nazwą pliku w oknie dialogowym zapisywania.

minimulina
źródło
Przepraszam, że wróciłem późno. Wypróbowałem twoją odpowiedź i pobiera plik, ale zawiera błędne dane .. :(
Eugene Yu
1
to zadziałało dla mnie ... data = "data:application/octet-stream;charset=utf-8," + encodeURIComponent(JSON.stringify(data)); window.open(data); po prostu pobiera plik jako „pobierz”, ale dane, które zostały zdefiniowane, a następnie zakodowane w formacie URI są takie, jakie powinny być.
Jason Wiener
0

Jeśli wolisz fragment konsoli, raser niż nazwę pliku, możesz to zrobić:

window.open(URL.createObjectURL(
    new Blob([JSON.stringify(JSON)], {
      type: 'application/binary'}
    )
))
zb '
źródło