Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FileReader readAsText() async issues?

I have implemented the following code to parse a CSV via a <input type="file" /> selection:

export async function parse(file: File) {
  let content = '';
  const reader = new FileReader();
  reader.onload = function(e: any) {
    content = e.target.result;
  };
  await reader.readAsText(file);
  const result = content.split(/\r\n|\n/);
  return result;
}

If I run this code and put a breakpoint on the line where I declare result, it retrieves the contents of the file successfully. If I do not put any breakpoint, the content is empty. As you can see, I added await to the line where the reader reads the file as text, but it's still not working.

like image 681
im1dermike Avatar asked Jun 25 '18 14:06

im1dermike


People also ask

Is FileReader asynchronous?

The FileReader object lets web applications asynchronously read the contents of files (or raw data buffers) stored on the user's computer, using File or Blob objects to specify the file or data to read.

How does FileReader onload work?

The FileReader. onload property contains an event handler executed when the load event is fired, when content read with readAsArrayBuffer, readAsBinaryString, readAsDataURL or readAsText is available.

What does FileReader return?

The FileReader result property returns the file's contents. This property is only valid after the read operation is complete, and the format of the data depends on which of the methods was used to initiate the read operation.


3 Answers

await doesn't help here. readAsText() doesn't return a Promise.

You need to wrap the whole process in a Promise:

export function parse(file: File) {
  // Always return a Promise
  return new Promise((resolve, reject) => {
    let content = '';
    const reader = new FileReader();
    // Wait till complete
    reader.onloadend = function(e: any) {
      content = e.target.result;
      const result = content.split(/\r\n|\n/);
      resolve(result);
    };
    // Make sure to handle error states
    reader.onerror = function(e: any) {
      reject(e);
    };
    reader.readAsText(file);
  });
}
like image 54
zero298 Avatar answered Sep 20 '22 08:09

zero298


Here is the JSBin I have tried and it work like a charm.

function parse(file) {
  const reader = new FileReader();
  reader.readAsText(file);
  reader.onload = function(event) {
    // The file's text will be printed here
  console.log(reader.result)
  }
}

Updated:

I write you a Promise version.

async function parse(file) {
  const reader = new FileReader();
  reader.readAsText(file);
  const result = await new Promise((resolve, reject) => {
    reader.onload = function(event) {
    resolve(reader.result)
    }
  })
  console.log(result)
}
like image 40
jerry_p Avatar answered Sep 20 '22 08:09

jerry_p


To generalize @zero298's answer a tiny bit, here's the generic Promise-based wrapper around FileReader -

// get contents of a file as obtained from an html input type=file element
function getFileContents(file) {
  return new Promise((resolve, reject) => {
    let contents = ""
    const reader = new FileReader()
    reader.onloadend = function (e) {
      contents = e.target.result
      resolve(contents)
    }
    reader.onerror = function (e) {
      reject(e)
    }
    reader.readAsText(file)
  })
}

used like so -

async function parse(file) {
  const contents = await getFileContents(file)
  const result = contents.split(/\r\n|\n/)
  return result
}

or in the general case,

async function show(file) {
  const contents = await getFileContents(file)
  alert(contents)
}
like image 44
Brian Burns Avatar answered Sep 24 '22 08:09

Brian Burns