Reputation: 17
I have 2 elements on a page an input type="file" and a button when the button is clicked, I want to check if the file selected on the input element still exists or not. Let's just say the file gets deleted or renamed after after being selected and before the button was clicked.
Is there a way to do this? Even just a simple alert code whether it exists or not would be helpful.. thank you
Upvotes: 1
Views: 1007
Reputation: 296
You can actually do this without a fetch or XHR and without reading the entire file into memory. To do that we try to read a single byte from the file, and if it fails then you know the file no longer exists. When the file's size is zero the read will succeed regardless of whether the file still exists, but we can just use a fetch in that case.
async function fileExists(file) {
try {
if(file.size == 0) {
await fetch(URL.createObjectURL(file))
} else {
await file.slice(0, 1).arrayBuffer()
}
console.log("file exists")
return true
} catch(e) {
console.log("file deleted")
return false
}
}
Upvotes: 0
Reputation: 20414
Extending on @Kaiido answer: loading from URL like that will load the entire file. If the file is huge it will take very long time and/or will cause the browser to consume a lot of RAM. Tested on chrome with 8GB file - browser used around 8GB memory when using fetch. When using XHR it seemed not to eat up memory but the request took very long to complete. One possible workaround - check onprogress event of the XHR:
xhr.onprogress = function (e) {
if (e.loaded > 0) {
xhr.abort();
console.log("File still exists");
}
}
Upvotes: 0
Reputation: 136648
You can use the URL.createObjectURL
method which will create a direct pointer to your file on the disk.
Then to check whether it has been deleted/renamed or not, you can simply try to fetch it (either through the fetch
API, or through XHR).
let url;
inp.onchange = e => {
url = URL.createObjectURL(inp.files[0]);
btn.disabled = false;
}
btn.onclick = e => {
fetch(url)
.then((r) => console.log("File still exists"))
.catch(e => console.log("File has been removed or renamed"));
}
<input type="file" id="inp">
<button disabled id="btn">check if deleted</button>
ES5 version : (with a lot of quirks to handle... only tested in FF Safari and chrome)
var url;
inp.onchange = function(e) {
url = URL.createObjectURL(inp.files[0]);
btn.disabled = false;
}
btn.onclick = function(e) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url); // cache trick for Safari
xhr.responseType = 'blob';
var headers = { // Safari uses the cache...
"Pragma": "no-cache",
"Cache-Control": "no-store, no-cache, must-revalidate, post-check=0, pre-check=0",
"Expires": 0,
"Last-Modified": new Date(0), // January 1, 1970
"If-Modified-Since": new Date(0)
};
for (var k in headers) {
xhr.setRequestHeader(k, headers[k]);
}
xhr.onload = function(e) {
if (xhr.response.size) {
console.log("File still exists\n");
} else { // chrome fires the load event
console.log("File has been removed or renamed (load event)\n");
}
};
xhr.onerror = function(e) { // sometimes it fires an error
console.log("File has been removed or renamed (error event)\n");
};
try {
xhr.send();
} catch (e) { // sometimes it throws in FF
console.log("File has been removed or renamed (caught)\n");
}
}
<input type="file" id="inp">
<button disabled id="btn">check if deleted</button>
And fiddles for Safari which doesn't fetch BlobURIs from stacksnippet®'s null-origined iframes :
ES6, ES5
Upvotes: 3