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

3

16

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

  ...

}

Foison answered 15/8, 2019 at 22:28 Comment(2)
Any solution for this?Flowerlike
Still seeking a solutionTrumpetweed
N
2

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.

Nidianidicolous answered 22/5, 2020 at 8:48 Comment(3)
I can confirm that this works! The file selected from Google Drive threw an exception and the local file didn't. Depending on the state of the API, I could see this changing in the future, but for now it works. Interestingly, using the FileReader API to read the Drive file into an ArrayBuffer still provides the data without error.Foison
@JBrewer tried to use FileReader API which leads to another error instance of ProgressEvent name NotReadableError message the requested file could not be read, typically due to permission problems that have occurred after a reference to a file was acquired. . Yes it does work from time to time. Can you confirm that it does work properly always?Leeke
@Shandor Kostur - From my earlier comment, I didn't mean that the FileReader API worked, only that you could programatically access the raw data of the Google Drive file on your local browser, which is the opposite of what we want here. In this case, when trying to upload a file to Firebase storage use the method in the answer and not the FileReader API.Foison
A
0

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("");
    })
Aculeate answered 22/5, 2020 at 7:56 Comment(0)
D
0

This seems to be a known chrome bug; The issue has been open for more than 3 yrs now [2020].

Using blob, arrayBuffer or creating a local file using object or data URL does not solve this issue. Neither of the suggested approaches above help.

Please refer https://bugs.chromium.org/p/chromium/issues/detail?id=1063576

Google has moved it as a priority 2 issue.

Dichotomy answered 26/10, 2023 at 11:49 Comment(1)
While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From ReviewSemipostal

© 2022 - 2025 — McMap. All rights reserved.