防止 HTML 文件输入在使用 Android 的本机文件选择器时选择 Google Drive 中的文件
目前我正在开发一个允许用户将文件上传到 Firebase 存储的页面.在 Android 上通过 Google Chrome 打开网站并从标准 HTML 文件输入中选择要上传的文件时,它使用 Android 的本机文件选择器.
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.
在大多数情况下,用户会选择存储在设备本地的文件,但文件选择器也会显示他们的 Google 云端硬盘文件,并且当前不会阻止用户选择其中一个文件.该文件在 Javascript 中作为 File 对象返回,但是当尝试上传到 Firebase 存储时,它会引发错误:net::ERR_UPLOAD_FILE_CHANGED"并最终超出其重试限制.
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.
为了防止用户混淆,我想阻止用户在 Android 的文件选择器中选择 Google Drive 文件,或者至少认识到它无法上传并警告用户.
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.
我考虑检查输入元素返回的 File 对象,但没有任何迹象表明可以从 Google Drive 文件中区分本地文件.
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);
...
}
推荐答案
我不知道如何阻止文件选择器显示这些文件,我怀疑没有,但是如果您的代码可以很容易地检查将能够通过尝试读取将其发送到您的服务器.
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.
我们可以尝试只读取第一个字节,而不是读取整个文件,方法是对 File 对象进行切片.然后阅读它,我们可以简单地调用它的 arrayBuffer()
方法 将返回一个 Promise,要么在文件有效时解析,要么在我们无法访问文件时拒绝:
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.
还请注意,Blob.arrayBuffer()
是相当新的,可能需要一个 polyfill,它很容易在 Internet 上制作或找到.
Note also that Blob.arrayBuffer()
is quite recent and may require a polyfill, which is easily made or found on the internet.
相关文章