Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent HTML file input from selecting files in Google Drive while using Android's native file chooser

Currently I'm working on a page that allows users to upload a file to Firebase Storage. When opening the site through Google Chrome on Android and selecting a file for upload from a standard HTML file input, it uses Android's native file chooser.

In most cases, a user would choose a file stored locally on the device, but the file chooser also shows their Google Drive files and a user currently isn't prevented from selecting one of those files. The file is returned as a File object in Javascript, but when the upload to Firebase Storage is attempted it throws the error: "net::ERR_UPLOAD_FILE_CHANGED" and eventually exceeds it's retry limit.

To prevent confusion for the user, I'd like to prevent the user from selecting a Google Drive file in Android's file chooser, or at the very least recognize that it can't be uploaded and warn the user.

I considered checking the File object returned by the input element, but there isn't any indication to tell a local file from a Google Drive file.

<input type="file" id="upload_input" class="hide"/>
$("#upload_input").change(function(e) {
  if (!e.target.files) {
    return;
  }
  const file = e.target.files[0];
  uploadFile(file);
});


uploadFile(file) {

  ...

  const storageRef = firebase.storage().ref();
  const fileRef = storageRef.child(`${userID}/uploads/${file.name}`);
  const uploadTask = fileRef.put(file);

  ...

}

like image 477
J Brewer Avatar asked Aug 15 '19 22:08

J Brewer


1 Answers

I don't know a way to prevent the file picker to show these files, and I suspect there is none, but you can check quite easily if your code will be able to send it to your server by trying to read it.

Instead of reading the whole file, we can try to read only the first byte, by slicing the File object. Then to read it, we can simply call its arrayBuffer() method which will return a Promise, either resolving when the File is valid, or rejecting if we can't access the file:

const inp = document.querySelector('input');
inp.onchange = (evt) => {
  const file = inp.files[ 0 ];
  file.slice( 0, 1 ) // only the first byte
    .arrayBuffer() // try to read
    .then( () => {
      // success, we should be able to send that File
      console.log( 'should be fine' );
    } )
    .catch( (err) => {
      // error while reading
      console.log( 'failed to read' );
      inp.value = null; // remove invalid file?
    } );
};
<input type="file">

Note that even Files stored on disk may be modified by the user since they did pick it in your website, in that case, the upload would still fail. To handle this case too, you'd just have to perform the same exact test.

Note also that Blob.arrayBuffer() is quite recent and may require a polyfill, which is easily made or found on the internet.

like image 170
Kaiido Avatar answered Oct 02 '22 01:10

Kaiido