How to create a modified copy of a File object in JavaScript? - javascript

Properties of files received from an <input type="file"> are read-only.
For example, the following attempt to re-write file.name would either fail silently or throw TypeError: Cannot assign to read only property 'name' of object '#<File>'.
<input onchange="onchange" type="file">
onchange = (event) => {
const file = event.target.files[0];
file.name = 'foo';
}
Attempting to create a copy via Object.assign({}, file) fails (creates an empty object).
So how does one clone a File object?

My solution lay in the File constructor:
https://developer.mozilla.org/en-US/docs/Web/API/File#Implementation_notes
Which itself is an extension of Blob:
https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob
let file = event.target.files[0];
if (this.props.distro) {
const name = 'new-name-here' + // Concat with file extension.
file.name.substring(file.name.lastIndexOf('.'));
// Instantiate copy of file, giving it new name.
file = new File([file], name, { type: file.type });
}
Note the first argument to File() must be an array, not simply the original file.

You can use FormData.prototype.append(), which also converts a Blob to a File object.
let file = event.target.files[0];
let data = new FormData();
data.append("file", file, file.name);
let _file = data.get("file");

A more cross browser solution
The accepted answer works for me too in modern browsers, but unfortunately it does not work in IE11, since IE11 does not support the File constructor.
However, IE11 does support the Blob constructor so it can be used as an alternative.
For example:
var newFile = new Blob([originalFile], {type: originalFile.type});
newFile.name = 'copy-of-'+originalFile.name;
newFile.lastModifiedDate = originalFile.lastModifiedDate;
Source: MSDN - How to create a file instannce using HTML 5 file API?

Related

Attach a file to an input with javascript

I have an input to upload files
<input type="file" name="comment[video_file]" id="comment_video_file">
Is it possible to attach a file by JavaScript?
I have tried but it didn't work
let file = new File([videoBlob], "video.mp4", { type: "video/mp4" });
let element = document.getElementById("comment_video_file");
element.append("video", video);
If I console log the element after the append it looks like this
<input type="file" name="comment[video_file]" id="comment_video_file">
"video"
"[object File]"
</input>
It isn't possible to create a file and attach it to an HTML form input but using the FormData object you could send a generated file to the server as part of a post request.
Pulled from the MDN:
var formData = new FormData();
// JavaScript file-like object
var content = '<a id="a"><b id="b">hey!</b></a>'; // the body of the new file...
var blob = new Blob([content], { type: "text/xml"});
formData.append("webmasterfile", blob);
var request = new XMLHttpRequest();
request.open("POST", "http://example.com/submitform.php");
request.send(formData);
Which should get you the same result of a file generated by JS sent to the server.
MDN article on file inputs states:
You can set as well as get the value of HTMLInputElement.files in all modern browsers; this was most recently added to Firefox, in version 57
I tried setting input.files to a files property from a drag-n-drop files event and it worked. Though I'm not sure if it's possible to do it with a manually created File.
If this won't work, try sending an XMLHttpRequest with FormData with a file Blob, as advised in other answers/comments.

How to read a file content in Native File System API

I am trying to read the content of the XML file. Probably this is basic JS stuff, but I seem can't make it work.
I am using Chrome's experimental Native File System API to read folders in folder:
const opts = {type: 'open-directory'};
handle = await window.chooseFileSystemEntries(opts);
const entries = await handle.getEntries();
...
Then, later on in the code I am entering one of the folders from the main directory and trying to read the file in it. The file system strucure is like this:
Directory > subdirectory > file
and the second part of the code looks like this:
var subdirHandle = await handle.getDirectory(oneOfTheFolders);
var xmlFile = await subdirHandle.getFile('subject.xml');
xmlDoc = domParser.parseFromString(xmlFile, "text/xml");
parsedNumber = document.evaluate(myXpathFce('nodeInXML'), xmlDoc, null, XPathResult.ANY_TYPE, null).iterateNext();
if(parsedNumber.childNodes.length >0){
...
I believe the issue is here var xmlFile = await subdirHandle.getFile('subject.xml'); with the file reading. If I loaded the file straight from the Input and used FileReader(), I was able to get the content and parse it, but with the 'directory' approach I am getting null (for the evaluated document) like this Uncaught (in promise) TypeError: Cannot read property 'childNodes' of null
Edit here is what I get in console for the xmlFile variable. I just need to get the content (XML in text format) from this
I noticed you're saving the File object in the xmlFile variable and passing it directly into the parseFromString method.
You cannot parse a document object from a File object directly. You should first read the string from the File object using a FileReader. You can read the string from a File object with the await keyword using the readFileAsync function below:
function readFileAsync(file) {
return new Promise((resolve, reject) => {
let reader = new FileReader();
reader.onload = () => {
resolve(reader.result);
};
reader.onerror = reject;
reader.readAsText(file);
})
}
var file = await handle.getFile();
var text = await readFileAsync(file);
var xmlDoc = domParser.parseFromString(text, "text/xml");
For obtaining the contents of a FileSystemFileHandle, call getFile(), which returns a File object, which contains a blob. To get the data from the blob, call one of its methods (slice(), stream(), text(), arrayBuffer()).

Converting a Blob object into a File, for Ms Edge

I have a Blob Object, which is an image and I am trying to convert into a file object, But it shows errors in MS edge version 41. I am using formdata in 1st two attempts for the same
Attempt 1
fd.set('file', blobObj, fileName);
return (fd.get('file'));
This resulted in an error
object doesn't support this property or method 'set'
Attempt 2
I replaced set with append and then I got this
object doesn't support this property or method 'get'
Attempt 3
I replaced formdata entirely with a new logic which looked like this
let fileObject = new File([u8arr], fileName, { type: mime });
and I got an error saying
object doesn't support this action
Is there any other method that can be used? Can I directly use blob as a file?
AFAIK, Your third approach seems to be working ,
Try once by hard-coding the mime type to "image/jpeg" / "image/png" and include the date modeified and then verify once
var fileInstance = new File([blob], "FileName",{type:"image/jpeg", lastModified:new Date()})
If you are displaying it in javascript you should use something like this:
var URL = window.URL || window.webkitURL;
var url_instance = URL.createObjectURL(blob);
var image_source = new Image();
image_source.src = url_instance;
document.body.appendChild(image_source);
A File object is a specific kind of a Blob, it's just missing the two properties: lastModifiedDate and name(file name property).
So, you could convert the blob object to file object using the following code:
var blobtoFile = function blobToFile(theBlob, fileName) {
//A Blob() is almost a File() - it's just missing the two properties below which we will add
theBlob.lastModifiedDate = new Date();
theBlob.name = fileName;
return theBlob;
}
var file = blobtoFile(blob, "test.png");
More detail information about using the above code, please check this sample.
Besides, please check the FormData Method Browser compatibility, from it we can see most of the methods support Microsoft Edge 44+(EdgeHTML 18+, more detail, please check this article).
So, if you want to use FormData set or get method, please try to upgrade the Windows version(Microsoft Edge is part of the operating system and can't be updated separately. It receives updates through Windows Update, like the rest of the operating system.). Otherwise, you could use a JavaScript Object to store the blob or file object.
Detail updated steps as below: Select Start > Settings > Update & Security > Windows Update , then select Check for updates and install any available updates.

image url to file() object using js

For a registration module in my vue app I let users upload images in a form. I upload these images to my storage and save the download url of this photo in the registration. When editing a registration I need to get the photo's out of the storage which is simple since I got the url. But I need it to be a file() object. I have found ways to turn it into a blob but it needs to be a file. How can I do this?
It can be done by requesting a blob and generating a File object. It is necessary to specify the MIME type of the blob.
const urlToObject= async()=> {
const response = await fetch(image);
// here image is url/location of image
const blob = await response.blob();
const file = new File([blob], 'image.jpg', {type: blob.type});
console.log(file);
}
The ES6 way, with Promise:
const blobUrlToFile = (blobUrl:string): Promise<File> => new Promise((resolve) => {
fetch(blobUrl).then((res) => {
res.blob().then((blob) => {
// please change the file.extension with something more meaningful
// or create a utility function to parse from URL
const file = new File([blob], 'file.extension', {type: blob.type})
resolve(file)
})
})
})
Since you are letting the user upload a file, you already have the file as a File object.
But if you wanna convert it to a blob for making some edits and convert it back to a File object, you can use the File() constructor for converting a blob to a File.
const file = new File([blob], "imagename.png");
Also, notice that the File() constructor takes an array of blobs as argument and not a single blob.

Append string as file upload using FormData

I was wondering if I can upload string as file using form data. I believe there should be some File object, that can have value, filename and maybe also mime-type set.
Pseudo code:
var file = new File();
file.name = "file.txt";
file.mimeType = "text/plain";
file.value = "blah blah\nsecond line";
var data = new FormData();
data.append(file);
works fine for me
const blob = new Blob(['blah blah\nsecond line'], {type : 'text/plain'})
formData.append('file', blob, 'file.txt')
There is indeed an object called File(on modern browsers), but you cannot create a new instance of it, out of security issues. Therefore, what you seek is not possible.

Categories

Resources