Ivan Revzin

Download JSON formed in Browser

In my todo app I stored all data in a JSON in LocalStorage.

To move from my old domain to a new one I needed export/import functionality.

To do the export I used JSON.stringify, btoa and an anchor tag with href pointing to a Data URI scheme:

function ExportTodos({ todos = [] }) {
  const todos64 = [todos]
    .map((val) => JSON.stringify(val)) // js object to string
    .map(btoa) // string to base64
  [0];

  return (
    <a href={`data:application/json;base64,${todos64}`}>
      Export
    </a>
  );
}

But the btoa function crushed:

btoa('Привет, world!')
// Uncaught DOMException: String contains an invalid character

I started to look for the reason. Turns out btoa has a unicode problem.

I found some solutions:

Using Blob there were 2 ways to form a download URL:

  1. using URL.createObjectURL()
  2. using FileReader.readAsDataURL

The first method was simpler so I used it. But there was a potential issue with memory, but simple useEffect's cleanup function solved it.

function ExportTodos({ todos = [] }) {
  const [downloadURL, setDownloadURL] = useState('');

  useEffect(() => {
    const blob = new Blob(
      [JSON.stringify(todos)],
      { type: 'application/json' }
    );
    const downloadURL = URL.createObjectURL(blob);
    setDownloadURL(downloadURL);

    return () => {
      URL.revokeObjectURL(downloadURL)
    }
  }, [todos]);

  return (
    <a
      download={`todos.json`}
      href={downloadURL}
    >
      Export
    </a>
  );
}

Full ExportTodos component file is here.

#frontend