13
votes

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);

  ...

}

2
Any solution for this?Yuliwee
Still seeking a solutionrick6

2 Answers

2
votes

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.

0
votes

It deals with file modification properties. input tag is getting a file with the same name but it's modified properties are not changed.

The only solution I get is -

You need to reset the input field filed of input tag on the user click as it by default getting empty on click.

eg; Using jquery library to reset input tag

   document.querySelector("#upload_input").addEventListener("click", 
    function(event){
       $("#upload_input").val("");
    })