React:write to json file or export/download [no server]
Asked Answered
C

3

19

I got really confused with file I/O in JS/TS. most examples I see works with DOM and has browser-based solutions.

Also, I did not understand how to make fs work, it seems to need a webpack config, where I use CRA and do not want to eject.

in a React component I want to fetch some data from a server then save them as a JSON file in the project folder (the same path, root, public folder, no matter) or directly download (no button needed).

//data type just in case
inteface IAllData{ name:string; allData:IData[];}

so after fetching some data want to save them to name.json

public componentDidMount(){
   this.fetchData().then(()=>this.saveData())
}

public async fetchData(){/* sets data in state*/}

public saveData(){
    const {myData}=this.state;
    const fileName=myData.name;
    const json=JSON.stringify(myData);
    const blob=new Blob([json],{type:'application/json'})
    /* How to write/download this blob as a file? */
}

here trying window.navigator.msSaveOrOpenBlob(blob, 'export.json'); did not work

note: I know it has security risks, it is not for production. save the file in the project folder is preferred but a download is totally ok.

Centrality answered 10/4, 2019 at 13:10 Comment(0)
P
42

I had a blob containing data and I had found a solution on stackoverflow and manipulated a bit, and succeded to download as a xlsx file. I am adding my code below, it might help you, too.

const blob =  await res.blob(); // blob just as yours
const href = await URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = href;
link.download = "file.xlsx";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);

EDIT: You can use the function below, but be sure to switch out fileName and myData from this.state to something that will work in your application.

const downloadFile = () => {
  const { myData } = this.state; // I am assuming that "this.state.myData"
                                 // is an object and I wrote it to file as
                                 // json

  // create file in browser
  const fileName = "my-file";
  const json = JSON.stringify(myData, null, 2);
  const blob = new Blob([json], { type: "application/json" });
  const href = URL.createObjectURL(blob);

  // create "a" HTLM element with href to file
  const link = document.createElement("a");
  link.href = href;
  link.download = fileName + ".json";
  document.body.appendChild(link);
  link.click();

  // clean up "a" element & remove ObjectURL
  document.body.removeChild(link);
  URL.revokeObjectURL(href);
}

More documentation for URL.createObjectURL is available on MDN. It's critical to release the object with URL.revokeObjectURL to prevent a memory leak. In the function above, since we've already downloaded the file, we can immediately revoke the object.

Each time you call createObjectURL(), a new object URL is created, even if you've already created one for the same object. Each of these must be released by calling URL.revokeObjectURL() when you no longer need them.

Browsers will release object URLs automatically when the document is unloaded; however, for optimal performance and memory usage, if there are safe times when you can explicitly unload them, you should do so.

Petta answered 10/4, 2019 at 13:25 Comment(0)
H
19

For the ones like me here that are looking for an easier solution when you already have your JSON as a variable:

         <button
            href={`data:text/json;charset=utf-8,${encodeURIComponent(
              JSON.stringify(YOURJSON)
            )}`}
            download="filename.json"
          >
            {`Download Json`}
          </button>
Hawken answered 28/7, 2020 at 16:8 Comment(3)
this code didn't work for me until I changed <button> to <a>Tjader
@user2584621's solution worked for me. And is there any limitation for the data length? For me, it's going to be a huge file.Chadd
There is some information on data uri limits here: #695651Washer
O
4
<button
  type="button"
  href={`data:text/json;charset=utf-8,${encodeURIComponent(
   JSON.stringify(YOURJSON)
  )}`}
  download="filename.json"
  >
 {`Download Json`}
 </button>

if you are using the Loic V method just ad the type for the button on the button element and should work just fine.

Offbeat answered 28/1, 2021 at 12:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.