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.
Related
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.
I'm aware of the security reasons that local file cannot be set as a file programmatically to input field.
Suppose I've this image as base64:
var bus = "data:image/gif;base64,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";
I want to set it to an input type file programmatically. Please do not suggest to send this as a string to server because I cannot control the API at the other end.
I tried to convert it to blob in my code and set it to input field as:
myInputField.value = blob;
but it throws security exception:
Uncaught DOMException: Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string.
Is it possible via javascript? I, by no means, want to involve the file chooser dialog box.
Thank you.
Not possible. An HTML file input can only point to a file that actually exists on the computer -- it can't point to an arbitrary chunk of data.
No you can't set the value of a file input*, except to clear it, but it sounds that it's not what you need anyway.
Instead, convert this dataURI to a Blob, then append this Blob to a FormData and finally post this FormData.
Your image will be sent as multipart like you seem to need.
function dataURItoBlob(data) {
var binStr = atob(data).split(',')[1],
len = binStr.length,
arr = new Uint8Array(len);
for (var i = 0; i < len; i++) {
arr[i] = binStr.charCodeAt(i);
}
return new Blob(arr);
}
var dataURI = 'data:image/....';
var blob = dataURItoBlob(dataURI);
// you can even pass a <form> in this constructor to add other fields
var formData = new FormData();
formData.append('yourFileField', blob, 'fileName.ext');
var xhr = new XMLHttpRequest();
xhr.open('post', yourServer);
xhr.send(formData);
// now you can retrieve your image as a File/multipart with the 'yourFileField' post name
*Actually now you can do it, but it's still rather hackish.
You can try like below one. Convert your sting to Blob and create file type with created Blob.
var debug = {hello: "world"};
var blob = new Blob([JSON.stringify(debug, null, 2)], {type : 'application/json'});
var outputfile=new File([blob], "filename")
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?
I use event.clipboardData to get image from clipboard, and then upload it server, code:
var items = e.clipboardData.items;
for(var i=0;i<items.length;i++)
{
if(items[i].type.indexOf("image")!=-1)
{
var blob=items[i].getAsFile();
var data = new FormData();
data.append("ImageFileField",blob);
_post2("url...",data);
}
}
NOTE: _post2() is a function using XMLHttpRequest to do upload works.
Above code work fine, the image from clipboard can upload to my server directly.
BUT I found a problem, the filename of image upload to server is fixed as "blob", can I modify the filename before upload to server?
This is the upload data detail:
Request Payload
------WebKitFormBoundaryW0NQVOkdrfkYGWV3
Content-Disposition: form-data; name="%%File.48257279001171c9.2c36671da7f1b6c9482575de002e1f14.$Body.0.3D8"; filename="blob"
Content-Type: image/png
------WebKitFormBoundaryW0NQVOkdrfkYGWV3--
According to FormData, you should be able to add a filename parameter to your data.append() call as follows:
data.append("ImageFileField", blob, "imageFilename.png");
i faced same problem that during upload, file name not a assign to multipart with blob object but after a lots of google and RND . i find simple and best approach for this problem.
let file = event.target.files[0];
this.fileName = file.name; // Like : abc.jpeg
this.croppedImage = file //blob object after cropping
const formData = new FormData();
formData.append('file',this.croppedImageSend,this.fileName);
this.http.post(url, formData, headersOptions)
if you want to modify the filename in the blob itself, just add a key called "name"
blob.name = "imageFilename.png"
After that your JS functions should be able to pick it up. I'm using jQuery File Upload and this works with it.
I am currently uploading images pasted from the clipboard with the following code:
// Turns out getAsFile will return a blob, not a file
var blob = event.clipboardData.items[0].getAsFile(),
form = new FormData(),
request = new XMLHttpRequest();
form.append("blob",blob);
request.open(
"POST",
"/upload",
true
);
request.send(form);
Turns out the uploaded form field with receive a name similar to this: Blob157fce71535b4f93ba92ac6053d81e3a
Is there any way to set this or receive this file name client side, without doing any server side communication?
For Chrome, Safari and Firefox, just use this:
form.append("blob", blob, filename);
(see MDN documentation)
Adding this here as it doesn't seem to be here.
Aside from the excellent solution of form.append("blob",blob, filename); you can also turn the blob into a File instance:
var blob = new Blob([JSON.stringify([0,1,2])], {type : 'application/json'});
var fileOfBlob = new File([blob], 'aFileName.json');
form.append("upload", fileOfBlob);
Since you're getting the data pasted to clipboard, there is no reliable way of knowing the origin of the file and its properties (including name).
Your best bet is to come up with a file naming scheme of your own and send along with the blob.
form.append("filename",getFileName());
form.append("blob",blob);
function getFileName() {
// logic to generate file names
}
That name looks derived from an object URL GUID. Do the following to get the object URL that the name was derived from.
var URL = self.URL || self.webkitURL || self;
var object_url = URL.createObjectURL(blob);
URL.revokeObjectURL(object_url);
object_url will be formatted as blob:{origin}{GUID} in Google Chrome and moz-filedata:{GUID} in Firefox. An origin is the protocol+host+non-standard port for the protocol. For example, blob:http://stackoverflow.com/e7bc644d-d174-4d5e-b85d-beeb89c17743 or blob:http://[::1]:123/15111656-e46c-411d-a697-a09d23ec9a99. You probably want to extract the GUID and strip any dashes.
Haven't tested it, but that should alert the blobs data url:
var blob = event.clipboardData.items[0].getAsFile(),
form = new FormData(),
request = new XMLHttpRequest();
var reader = new FileReader();
reader.onload = function(event) {
alert(event.target.result); // <-- data url
};
reader.readAsDataURL(blob);
It really depends on how the server on the other side is configured and with what modules for how it handles a blob post. You can try putting the desired name in the path for your post.
request.open(
"POST",
"/upload/myname.bmp",
true
);
Are you using Google App Engine?
You could use cookies (made with JavaScript) to maintain a relationship between filenames and the name received from the server.
When you are using Google Chrome you can use/abuse the Google Filesystem API for this. Here you can create a file with a specified name and write the content of a blob to it. Then you can return the result to the user.
I have not found a good way for Firefox yet; probably a small piece of Flash like downloadify is required to name a blob.
IE10 has a msSaveBlob() function in the BlobBuilder.
Maybe this is more for downloading a blob, but it is related.