2

If I have an input:

<input type="file" id="upload" onchange="getFile(this)">

And my user will upload a JSON file (as plaintext, so I will have to use JSON.parse()), how can I take this file and actually get the data via getFile()

In getFile(element), I've tried using element.files[0] but that doesn't seem to contain the actual data. I've also looked here, here, and here, but none of these solve my problem. This resource on MDN seems promising, but I don't really get it.

I would like a solution involving either URL.createObjectURL() or FileReader().

Also, before anyone posts this in the comments, I do understand that these solutions do not work on all browsers, and I would like to do this from the frontend.

3 Answers 3

7

You could take advantage of the Response constructor and call .json() on any blob/file.

function getFile (elm) {
  new Response(elm.files[0]).json().then(json => {
    console.log(json)
  }, err => {
    // not json
  })
}

Alternative method using the new read methods on blob.prototype[...]

new Blob(['1']).text().then(JSON.parse).then(console.log)
// handles emptying file input with ?.
elm.files[0]?.text().then(JSON.parse).then(console.log)

I guess for larger files response.json might be faster/better since it can parse the content in background and not block the main UI unlike JSON.parse

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, it works! I like this answer's brevity and the fact that it also handles errors more easily. Although it didn't use either of those methods I mentioned, I'd argue it's a better solution.
This is great! Far more concise than using FileReader.
2

I think you need this api:

FileReader Api From MDN

JSON#parse()

View In Stackblitz

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Read Text</title>
  <style>
    div {
     margin-top: 30px;
     border: solid 1px black;
     padding: 5px;
    }
  </style>
  <script>
    function processFiles(files) {
      var file = files[0];

      var message = document.getElementById("message");
      message.innerHTML = "File Name:" + file.name + "<br>";
      message.innerHTML += "File Size:" + file.size + "<br>";
      message.innerHTML += "File Type:" + file.type + "<br>";

      var reader = new FileReader();
      reader.onload = function (e) {
        var output = document.getElementById("fileOutput");  
        // parse string to json 
        output.textContent = JSON.parse(e.target.result);
      };
      reader.readAsText(file);
    }
  </script>
</head>
<body>
  <input id="fileInput" type="file" size="50" onchange="processFiles(this.files)">
  <div id="message"></div>
  <div id="fileOutput"></div>
</body>
</html>

1 Comment

Might consider adding a try/catch in case JSON.parse() throws error from bad file
0

I am using React with TypeScript enabled, and below is what helps me through.

The readJsonFile() should be usable to plain JS without types, and I recommend using the new text() method - save much hassle:

// REMARK: <T> as generic type and `error` is of unknown type;
// remove both parts for plain JS solution.
const readJsonFile = async <T = unknown>(file: Blob): Promise<T | unknown> => {
  try {
    return JSON.parse(await file.text());
  catch (error) {
    // ...or other operations
    return error;
  }
}

const handleFileUpload = async (event: ChangeEvent<HTMLInputElement>) => {
  if (event.target.files) {
    // REMARK: Access util function,
    const jsonData = await readJsonFile(event.target.files[0]);
    // ...other operations
  }
}

Then in your <input> element:

<input type="file" accept=".json" onChange={handleFileUpload} />

This will work fine, and the JSON file will be parsed.

For multiple files:

const handleFileUpload = async (event: ChangeEvent<HTMLInputElement>) => {
  const files = event.target.files;
  
  if (files && files.length > 0) {
    const results = await Promise.all(
      Array.from(files).map(file => readJsonFile(file))
    );

    results.forEach((parsedData, index) => {
      // ...other operations
    });
  }
};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.