14

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.

Amir-Mousavi
  • 3,585
  • 8
  • 52
  • 101

3 Answers3

26

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: I have written a function for your case, you can use below function, but be careful about "fileName" (not in "this.state" object in my case) and "myData" object that is stored in "this.state" object.

const downloadFile = async () => {
  const {myData} = this.state; // I am assuming that "this.state.myData"
                               // is an object and I wrote it to file as
                               // json
  const fileName = "file";
  const json = JSON.stringify(myData);
  const blob = new Blob([json],{type:'application/json'});
  const href = await URL.createObjectURL(blob);
  const link = document.createElement('a');
  link.href = href;
  link.download = fileName + ".json";
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
}
safakeskin
  • 526
  • 4
  • 15
14

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>
Loïc V
  • 357
  • 4
  • 8
2
<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.