I wrote the following code to check whether the uploaded file exists or not using HTML5 file API.
The following JavaScript code has been mapped to it is as follows:
function addDoc() {
var file=document.getElementById("myFile").files[0]; //for input type=file
var reader=new FileReader();
reader.onload = function(e) {}
reader.readAsText(file);
var error = reader.error;
var texte=reader.result;
document.getElementById("DisplayText").innerText=reader.result; /*<p id="DisplayText>*/
}
After browsing a file from local system I tried to delete the "browsed" document form the folder before clicking on addDoc()
. After clicking the button I could still see Filereader.result
is not null and could display all the content.
Can someone explain on how the Filereader works? Is it that the FileReader gets bound as soon as the file is browsed?
Also can we check whether the system Readonly Attribute with FileReader similar to Java File.canread()
?
Could someone suggest on this? I have IE11 to test the code.
FileReader
load
event sets the .result
value asynchronously. To access the .result
use load
or loadend
event.
When a file has been selected at <input type="file">
Choose File
or Browse...
UI, deleting file at local filesystem should not effect the File
object at FileList
returned by .files
call. See 2.9.2. Transferable objects, 6.7.3 The DataTransfer interface.
4. The Blob Interface and Binary Data
Each
Blob
must have an internal snapshot state, which must be initially set to the state of the underlying storage, if any such underlying storage exists, and must be preserved throughstructured clone
. Further normative definition ofsnapshot state
can be found forFile
s.
2.9.8 Monkey patch for Blob and FileList objects
This monkey patch will be removed in due course. See w3c/FileAPI issue 32.
Blob
objects arecloneable objects
.
Each
Blob
object's [[Clone
]] internal method, given targetRealm and ignoring memory, must run these steps:If this is
closed
, then throw a"DataCloneError"
DOMException
.Return a new instance of this in targetRealm, corresponding to the same underlying data.
FileList
objects are cloneable objects.Each
FileList
object's[[Clone]]
internal method, given targetRealm and memory, must run these steps:
Let output be a new
FileList
object in targetRealm.For each file in this, add ?
[StructuredClone][15](_file, targetRealm, memory_)
to the end of the list ofFile
objects of output.Return output.
Selecting read-only files or folders at webkit and firefox browsers
At chrome, chromium if read-only permission is set for file at local filesystem and user selects file at <input type="file">
element, where FileReader
is used to read file, an error is thrown at FileReader
, generated from FileReader
progress
event.
If a Blob URL
is set to the same file object, the blob:
URL will not return the the read-only file at request to the Blob URL
.
Selection of folder where folder permission is set to read-only
At chrome, chromium where webkitdirectory
attribute is set and folder is selected with read-only permission FileList
.length
of event.target.files
returned 0
; event.target.files.webkitGetAsEntry()
is not called, "No file chosen"
is rendered at <input type="file">
shadowDOM
. When a folder is dropped at <input type="file">
or element where droppable
attribute set, the directory .name
and .path
of the read-only folder is displayed at drop
event.dataTransfer
.
When user drops file or folder at <textarea>
element, where no drop
event is attached beforeunload
event is called and a prompr is displayed at UI
Do you want to leave this site? Changes you made may not be saved. <Stay><Leave> // <buttons>
At firefox version 47.0b9 with allowdirs
attribute is set at <input type="file">
element, where user clicks "Choose folder.."
<input>
, the folder .name
and .path
of the parent folder are accessible at .then()
chained to event.target.getFilesAndDirectories()
. The files or folders contained within the selected folder are not returned when recursively iterating Directory
entries; an an empty string is returned.
If user clicks "Choose file..."
<input>
and a folder is selected without read-only permission set, when the folder at file manager is clicked, the files in the folder are listed.
Where a folder is selected where read-only permission is set an alert()
notification is rendered at UI displaying
Could not read the contents of <directory name> Permission denied
*nix OS
When user drops folder at <textarea>
element, where no drop
event is attached, the full path to the folder at user filesystem file:
protocol is exposed. The paths to the files contained within the folder are not also set as .value
; e.g.,
"file:///home/user/Documents/Document/"
When a file is dropped at <textarea>
element, where not drop
event is attached, the full path to the file at user filesystem is set as .value
of <textarea>
; that is,
"file:///home/user/Documents/Document/MyFileFullPathDisplayedAtTextAreaValue.txt"
If multiple files are selected and dropped at <textarea>
element, all of the full file paths are set as .value
of <textarea>
, delineated by new line character \n
"file:///home/user/Documents/Document/MyFileFullPathDisplayedAtTextAreaValue1.txt"
"file:///home/user/Documents/Document/MyFileFullPathDisplayedAtTextAreaValue2.txt"
..
Where an XMLHttpRequest()
is made for the file path and error is logged at console
NS_ERROR_DOM_BAD_URI: Access to restricted URI denied
When set as .src
of an <img>
element with .crossOrigin
set to "anonymous"
the img
error
event handler is called
At call to window.open()
with full path set at first parameter
Error: Access to '"file:///home/user/Documents/Document/MyFileFullPathDisplayedAtTextAreaValue.png"' from script denied
4.10.5.1.18. File Upload state (type=file
)
For historical reasons, the
value
IDL attribute prefixes the file name with the string "C:\fakepath\
". Some legacy user agents actually included the full path (which was a security vulnerability). As a result of this, obtaining the file name from thevalue
IDL attribute in a backwards-compatible way is non-trivial.
4.10.5.4. Common <input>
element APIs
filename
On getting, it must return the string "C:\fakepath\" followed by the name of the first file in the list of
selected files
, if any, or the empty string if the list is empty. On setting, if the new value is the empty string, it must empty the list ofselected files
; otherwise, it must throw an "InvalidStateError
"DOMException
.NOTE: This "fakepath" requirement is a sad accident of history. See the example in the File Upload state section for more information.
NOTE: Since
path components
are not permitted in file names in the list ofselected files
, the "\fakepath\
" cannot be mistaken for a path component.
4.10.5.1.18. File Upload state (type=file
)
Path components
When an
<input>
element’stype
attribute is in theFile Upload
state, the rules in this section apply.The
<input>
elementrepresents
a list ofselected files
, each file consisting of a file name, a file type, and a file body (the contents of the file).File names must not contain
path components
, even in the case that a user has selected an entire directory hierarchy or multiple files with the same name from different directories. Path components, for the purposes of theFile Upload
state, are those parts of file names that are separated by U+005C REVERSE SOLIDUS character () characters.
Bug report https://bugzilla.mozilla.org/show_bug.cgi?id=1311823
Following comment by Neal Deakin at bug report
I think the steps referred to are:
- Open data:text/html,
- Drag a file from the desktop to the textarea
I can reproduce this on Linux, but not on Windows or Mac.
The hunch above is correct; Linux is including the data as a url and plaintext as well.
dropped files at data:
prototcol data URI
at firefox, and chrome, chromium
data:text/html,<textarea></textarea>
The full path name of file or folder set as .value
of <textarea>
.
Dropping file at data URI
having only textarea
element at chrome, chromium replaces the data URI
with dropped file path at address bar, and loads the dropped file at the same tab, replacing the data URI
with the content of the dropped file.
plnkr http://plnkr.co/edit/ZfAGEAiyLLq8rGXD2ShE?p=preview
html
, javascript
to reproduce issue described above
<!DOCTYPE html>
<html>
<head>
<style>
body {
height: 400px;
}
textarea {
width: 95%;
height: inherit;
}
</style>
<script>
window.onload = function() {
var button = document.querySelector("#myfile + button");
var input = document.getElementById("myfile");
var display = document.getElementById("DisplayText");
var text = null;
function readFullPathToFileOnUserFileSystem(e) {
var path = e.target.value;
console.log(path);
var w = window.open(path, "_blank");
var img = new Image;
img.crossOrigin = "anonymous";
img.onload = function() {
document.body.appendChild(this);
}
img.onerror = function(err) {
console.log("img error", err.message)
}
img.src = path;
var request = new XMLHttpRequest();
request.open("GET", path.trim(), true);
request.onload = function() {
console.log(this.responseText)
}
request.error = function(err) {
console.log(err.message)
}
request.send();
}
display.addEventListener("input", readFullPathToFileOnUserFileSystem);
input.addEventListener("change", addDoc);
input.addEventListener("progress", function(event) {
console.log("progress", event)
});
button.addEventListener("click", handleText)
function addDoc(event) {
var mozResult = [];
function mozReadDirectories(entries, path) {
console.log("dir", entries, path);
return [].reduce.call(entries, function(promise, entry) {
return promise.then(function() {
console.log("entry", entry);
return Promise.resolve(entry.getFilesAndDirectories() || entry)
.then(function(dir) {
console.log("dir getFilesAndDirectories", dir)
return dir
})
})
}, Promise.resolve())
.catch(function(err) {
console.log(err, err.message)
})
.then(function(items) {
console.log("items", items);
var dir = items.filter(function(folder) {
return folder instanceof Directory
});
var files = items.filter(function(file) {
return file instanceof File
});
if (files.length) {
console.log("files:", files, path);
mozResult = mozResult.concat.apply(mozResult, files);
}
if (dir.length) {
console.log(dir, dir[0] instanceof Directory, dir[0]);
return mozReadDirectories(dir, dir[0].path || path);
} else {
if (!dir.length) {
return Promise.resolve(mozResult).then(function(complete) {
return complete
})
}
}
})
.catch(function(err) {
console.log(err)
})
};
console.log("files", event.target.files);
if ("getFilesAndDirectories" in event.target) {
return (event.type === "drop" ? event.dataTransfer : event.target)
.getFilesAndDirectories()
.then(function(dir) {
if (dir[0] instanceof Directory) {
console.log(dir)
return mozReadDirectories(dir, dir[0].path || path)
.then(function(complete) {
console.log("complete:", complete);
event.target.value = null;
});
} else {
if (dir[0] instanceof File && dir[0].size > 0) {
return Promise.resolve(dir)
.then(function(complete) {
console.log("complete:", complete);
})
} else {
if (dir[0].size == 0) {
throw new Error("could not process '" + dir[0].name + "' directory" + " at drop event at firefox, upload folders at 'Choose folder...' input");
}
}
}
}).catch(function(err) {
console.log(err)
})
}
var reader = new FileReader();
reader.onload = function(e) {
text = reader.result;
console.log("FileReader.result", text);
button.removeAttribute("disabled");
}
reader.onerror = function(err) {
console.log(err, err.loaded, err.loaded === 0, file);
button.removeAttribute("disabled");
}
reader.onprogress = function(e) {
console.log(e, e.lengthComputable, e.loaded, e.total);
}
reader.readAsArrayBuffer(file);
}
function handleText() {
// do stuff with `text`: `reader.result` from `addDoc`
display.textContent = text;
button.setAttribute("disabled", "disabled");
// set `text` to `null` if not needed or referenced again
text = null;
}
}
</script>
</head>
<body>
<input type="file" id="myfile" webkitdirectory directory allowdirs>
<button type="button" disabled>Add Document</button>
<br>
<br>
<textarea id="DisplayText"></textarea>
</body>
</html>
plnkr http://plnkr.co/edit/8Ovw3IlYKI8BYsLhzV88?p=preview
You can use change
event attached to #myfile
element to handle file selection action by user.
Substitute <textarea>
element for <p>
element to display result of load
event from .readAsText()
call.
To display .result
of FileReader
at click
at button
element, set variable text
to reader.result
within load
event of FileReader
at click
event at button
set .textContent
of #DisplayText
element to variable referencing previously set reader.result
.
<!DOCTYPE html>
<html>
<style>
body {
height: 400px;
}
textarea {
width:95%;
height: inherit;
}
</style>
<head>
<script>
window.onload = function() {
var button = document.querySelector("#myfile + button");
var input = document.getElementById("myfile");
var display = document.getElementById("DisplayText");
var text = null;
input.addEventListener("change", addDoc);
button.addEventListener("click", handleText)
function addDoc(event) {
var file = this.files[0]
var reader = new FileReader();
reader.onload = function(e) {
text = reader.result;
button.removeAttribute("disabled");
}
reader.onerror = function(err) {
console.log(err, err.loaded
, err.loaded === 0
, file);
button.removeAttribute("disabled");
}
reader.readAsText(event.target.files[0]);
}
function handleText() {
// do stuff with `text`: `reader.result` from `addDoc`
display.textContent = text;
button.setAttribute("disabled", "disabled");
// set `text` to `null` if not needed or referenced again
text = null;
}
}
</script>
</head>
<body>
<input type="file" id="myfile" accept="text/*">
<button type="button" disabled>Add Document</button><br><br>
<textarea id="DisplayText"></textarea>
</body>
</html>