Does fetch support multiple file upload natively? - javascript

Summary
I am trying to set my FormData properly using javascript.
I need to be able to upload jpg/png, but I might need to upload some other file types pdf/csv in the future using fetch.
Expected
I expect it to append the data to the form
Error
Working
This snippet is working fine:
const formData = new FormData(document.querySelector('form'));
formData.append("extraField", "This is some extra data, testing");
return fetch('http://localhost:8080/api/upload/multi', {
method: 'POST',
body: formData,
});
Not working
const formData = new FormData();
const input = document.querySelector('input[type="file"]');
formData.append('files', input.files);
Question
Does fetch support multiple file upload natively?

If you want multiples file, you can use this
var input = document.querySelector('input[type="file"]')
var data = new FormData()
for (const file of input.files) {
data.append('files',file,file.name)
}
fetch('http://localhost:8080/api/upload/multi', {
method: 'POST',
body: data
})

The issue with your code is in the lineformData.append('files', input.files);
Instead of that, you should upload each file running a loop with unique keys, like this
const fileList = document.querySelector('input[type="file"]').files;
for(var i=0;i<fileList.length;i++) {
formData.append('file'+i, fileList.item(i));
}
I have created a simple error fiddle here with your code. You can check its' submitted post data here, where you can see that no file has been uploaded.
At the bottom of the page you can find
.
I have corrected the fiddle here with the fix. You can check its'post data from the server, where it shows the details of the two files that I uploaded.

I mentioned this on a similar question: I had the same problem, but with a PHP backend. The unique formData keys work, but I found that the classic HTML notation worked just fine and simply results in an array on the server.
formData.append('file[]', data[i]);
I like that a lot better, since I can use the same methods to process this as with a classic <input type="file" multiple />.

Related

Save Multiple Canvas Created Images to Server via AJAX with JavaScript

I am working on a form set for a client. In a nutshell:
The forms are filled out by my client’s customers by selecting different options on each form.
Each form can have multiple instances, depending on the customer.
At the end of the process, the customer can opt to either sign one or all the forms digitally or decline to sign them digitally and at the end of the process the forms are printed out and signed manually.
To accomplish this, I’ve created a signature plugin written in jQuery. Once the customer fills out the forms, they are presented each form separately. To sign the form digitally they simply tap (click) the signature block, a dialog with a canvas element appears, they sign the form and save it, the signature appears in the form, and they move on to the next form.
Here is the portion of the code that stores the completed signature and adds the image to the form:
$.sig = {
signatures: {},
}
function signatureSave() {
var canvas = document.getElementById("sigcanvas"),
dataURL = canvas.toDataURL("image/png");
document.getElementById($.sig.target).src = dataURL;
$.sig.signatures[$.sig.target].url = dataURL;
$.sig.signatures[$.sig.target].hasSignature = true;
};
The function is only called if the signature is saved, if there is no signature, the $.sig.signatures[$.sig.target].hasSignature remains false and the system skips the object.
This all works as intended, almost.
My problem lies in the process used to save the form information. If the customer does not sign any forms digitally the form information is simply saved and the forms are printed out, no need to save any signatures.
If the customer signs at least one form, though, the signatures must be sent to the server using the FormData() object.
I’ve used the FormData object in other projects for the client successfully, but only when the customer uploads one or more images to the browser using the input file element. It’s a pretty simple process because the resulting images have a img.file property that I send to the server.
Not so with a canvas object. All I get is the .src property, an any attempt to use anything from the resulting .png image that is created in the function above results in either a “cannot use a blob” or some other error.
Now I know if I have a single image, I can send it using AJAX with the following:
$.ajax({
type: "POST",
url: "script.php",
data: {
imgBase64: dataURL
}
})
Problem is that I am sending from one to x number of signatures.
Edit: I forgot to add this in. This is the function that is supposed to create the FormData object used to send the signatures to the server (and where my problem lies):
function getUploadData() {
var upl = new FormData();
$.each($.sig.signatures, function (e, u) {
if (u.hasSignature == true && u.url != null) {
var im = new Image();
im.src = u.url;
upl.append(u.target, im, u.target + '.png');
}
})
return upl;
}
I've tried all the tricks and nothing is working. The var im = new Image(); as well as the following line are just my latest ill fated attempt.
Picture perfect would be the ability to save the image information in the $.sig.signatures object so I can simply loop through any signatures that are signed, add them as elements of the FormData object, and then send the FormData object as the data for the AJAX call. As stated before, I use this method in other projects and works fine.
Does anyone know a way to do this?
Please note:
The server-side AJAX processor functions correctly.
The signature process works correctly (customer signs canvas, signature is displayed, signature information is stored).
All I need is how to send multiple images created using the canvas element in a FormData object to the server.
I know the answer is staring me right in the face, but I am just not getting it. Any hints or suggestions would be greatly appreciated!
Edit: Just a note. I've searched the entire afternoon for this and have found entries that either deal with sending multiple files using FormData and AJAX - but the files are uploaded to the browser (not created using Canvas), or single files sent to the server that are created using Canvas, but nothing about sending multiple files sent using FormData and AJAX that are created using Canvas. Oje!
As stated, the answer was staring me in the face, but I didn't see it because was looking behind the wrong door. FormData has nothing to do with it (Homer Dope Slap!).
Since I already have the data stored in $.sig.signature for each signature, I just need to send the information to the server as the data in the AJAX function. I updated my function above as shown:
function getUploadData() {
var upl = {};
$.each($.sig.signatures, function (e, u) {
if (u.hasSignature == true && u.url != null) {
upl[e] = u.url;
}
})
return upl;
}
Since the form information is sent as JSON I just add the signature info to the object that contains the form information, JSON.stringify it and send it on its way. This should work because the information retrieved above are strings.
Server side will look something like this:
$info = json_decode( $_POST['info'] );
// Various validation routines and checks
foreach( $info->signatures as $sig=>$data ):
$data = str_replace('data:image/png;base64,', '', $data);
$data = str_replace(' ', '+', $data);
$img = base64_decode($data);
// Do some processing, file naming, database saving and other general dodads
$success = file_put_contents( $file, $img );
endforeach;
The above function is still concept, I am reworking some of the code but this should work.
Credit is given to this post for opening my eyes:
post sending base64 image with ajaxpost sending base64 image with ajax
So question answered and yeah, I deserve a dope slap, but all comes out right in the end.
CAVEAT: Works like a charm.

Is there a way to put Express.Multer.File into a FormData object?

I need help on finding how to put a file with a type Express.Multer.File into a FormData object.
Context: I'm receiving a file with a type Express.Multer.File (I'm using nestjs, so I used this part of the documentation : https://docs.nestjs.com/techniques/file-upload) that I need to put in a FormData, to send it to another API.
I tried form.append('randomword', file); but I get the following error :
TypeError: source.on is not a function
at Function.DelayedStream.create (xxx/delayed-stream/lib/delayed_stream.js:33:10)
at FormData.CombinedStream.append (xxx/combined-stream/lib/combined_stream.js:45:37)
...
I clearly sense that I'm doing it the wrong way, but I'm kinda lost on how to achieve something like this.
Could someone help me please ?
You can use buffer and originalname obtained from multer to create a new Form Data.
For example, this would be the solution when you have only one file sent via POST method:
router.post("/", multer().single("file"), async (req, res, next) => {
let data = new FormData();
data.append("yourFormDataKey", req.file.buffer, req.file.originalname)
//HERE GOES YOUR CODE WHERE YOU USE THE FORM DATA CREATED//
}
I found out a "workaround" for this method.
First, I get the file with the type Express.Multer.File.
Then I save it using createWriteStream from fs.
Finally, I pass it into the FormData object using createReadStream.
And done !
It isn't the best way to do this, but kind of a workaround, though.

How to download a file through a custom POST request with CasperJS

I am writing a crawler and needs to download file generated after a form request using POST.
I have successfully used this.download(url,'POST',Params) for regular forms.
One of the sites has many fields using the same name, thus preventing me from using the regular download method.
After trying a lot of things, I tried with $.ajax() and __utils.sendAJAX() to process the form like this:
response = this.evaluate(function(){
url=...
params = $('form#theirForm').serialize();
data = __utils__.sendAJAX(url, 'POST', params,false,{contentType:"application/x-www-form-urlencoded"});
return __utils__.encode(data);
});
function decode_base64(s) { var e={},i,k,v=[],r='',w=String.fromCharCode; var n=[[65,91],[97,123],[48,58],[43,44],[47,48]]; for(z in n){for(i=n[z][0];i<n[z][1];i++){v.push(w(i));}} for(i=0;i<64;i++){e[v[i]]=i;} for(i=0;i<s.length;i+=72){ var b=0,c,x,l=0,o=s.substring(i,i+72); for(x=0;x<o.length;x++){ c=e[o.charAt(x)];b=(b<<6)+c;l+=6; while(l>=8){r+=w((b>>>(l-=8))%256);} } } return r; }
casper.then(function() {
utils.dump(response);
fs.write("test.zip",decode_base64(response),'w');
});
The codes returns me base64 data which I convert and store in a test.zip file.
But I juste can't uncompress it, says it is corrupted.
I dump the data of a correct zip file =>
PK^C^D^T^#^H^#^H^#<F4><89><96>F^#^#^#^#^#^#^#^#^#^#^#^#?^#^#^#fourniture denr<E9>es alimentaires - dietetique infantile\CCAP.pdf<AC><BC>^ET\K<D3><F7>;^D<B7><U+0B81>^#<C1><99>^Y^F'^D<B7><E0>^D^ON<90><E0><EE><EE><EE>^Dwww'^P<9C>^D^H<EE>^^܂<C3>%'<CF>9<E7><C9><F7><U+07B5><BE>7<F7>f^SVOzf
Compared it with the first line of my file :
PK^C^D^T^#^H^#^H^#)_^M^#^#^#^#^#^#^#^#^#^#^#^#^#b^#^#^#fourniture denr<FD>es alimentaires - dietetique infantile\Bordereau de prix dietetique infantile.xlsx<FD>zuT<FD>I<FD><FD><FD><FD>^^4hp^M^D^M^R^H<FD>.<FD><FD>}p<FD>3<FD>kpw<FD>#pw^M<FD><FD>^R4<FD>Gv<FD>~<FD>[<FD><FD><FD><FD><FD>
Anyone has an idea of what could have gone wrong?
I have tried so many things (encoding tools, encoding settings, dumping from the chrome console to get pure base64, etc.)
I don't understand why it is related to latin-1 or utf8 encoding, since a website asks me to select which encoding to use. Tried both.
casper.download() happily accepts a serialized form instead of an object, so you can still use it. You just have to serialize the form in the page context beforehand:
var formData = casper.evaluate(function(){
return $('form#theirForm').serialize();
});
var url;
casper.download(url, targetFile, 'POST', params);
The only problem might be, that another mimeType is used: "text/plain; charset=x-user-defined".
In that case, you will have to recreate the whole cascade of functions that go into casper.download():
var url;
var response = casper.evaluate(function(url){
var params = $('form#theirForm').serialize();
var data = __utils__.sendAJAX(url, 'POST', params, false);
return __utils__.encode(data);
}, url);
var cu = require('clientutils');
fs.write("test.zip", cu.decode(response), 'wb');
"application/x-www-form-urlencoded" is used by default for __utils__.sendAJAX().

FormData created from an existing form seems empty when I log it [duplicate]

This question already has answers here:
How to inspect FormData?
(22 answers)
Closed 6 years ago.
I'm trying to get a set of keys (input names or similar) and values (input values) from a web form:
<body>
<form>
<input type="text" name="banana" value="swag">
</form>
<script>
var form = document.querySelector('form');
var formData = new FormData(form);
</script>
</body>
According to the FormData documentation, formData should contain the keys and values from the form. But console.log(formData) shows formData is empty.
How can I quickly get the data from the form using FormData?
JSFiddle
Update: the XHR spec now includes several more functions to inspect FormData objects.
FireFox has supported the newer functions since v39.0, Chrome is slated to get support in v50. Not sure about other browsers.
var form = document.querySelector('form');
var formData = new FormData(form);
for (var [key, value] of formData.entries()) {
console.log(key, value);
}
//or
console.log(...formData)
I faced the same problem as well. I wasn't able to see on the console. You need to add the following to the ajax request, so the data will be sent
processData: false, contentType: false
But console.log(formData) shows formData is empty.
What do you mean by "empty"? When I test this in Chrome it shows ‣ FormData {append: function} ... which is to say it's a FormData object, which is what we expected. I made a fiddle and expanded to code to this:
var form = document.querySelector('form'),
formData = new FormData(form),
req = new XMLHttpRequest();
req.open("POST", "/echo/html/")
req.send(formData);
...and this is what I saw in the Chrome Developer Tools Network panel:
So the code is working as expected.
I think the disconnect here is that you're expecting FormData to work like a vanilla JavaScript object or array and let you directly look at and manipulate its contents. Unfortunately it doesn't work like that—FormData only has one method in its public API, which is append. Pretty much all you can do with it is create it, append values to it, and pass it to an XMLHttpRequest.
If you want to get the form values in a way you can inspect and manipulate, you'll have to do it the old-fashioned way: by iterating through the input elements and getting each value one-by-one—or by using a function written by someone else, e.g. jQuery's. Here's an SO thread with a few approaches to that: How can I get form data with JavaScript/jQuery?
As per MDN documentation on FormData
An object implementing FormData can directly be used in a for...of structure, instead of entries(): for (var p of myFormData) is equivalent to for (var p of myFormData.entries()).
Iterating over FormData.entries() didn't worked for me.
Here is what I do to check if formData is empty or not:
var isFormDataEmpty= true;
for (var p of formData) {
isFormDataEmpty= false;
break;
}
As iterating over formData gives you the uploaded file, you can use it for getting the file name, file type validation, etc.

Jquery: Push FormData?

Is there a way to add additional data to a formdata element that handles a file upload? I know formdata doesn't support .push()?
$("frm").submit(function (e) {
e.preventDefault();
var data = new FormData($(this)[0]);
});
If I'm understanding your question correctly, you want to add extra keys and values to the FormData object after taking them from the form. If so, yes you can! It uses the append method:
data.append('SomeField', 'SomeValue');
You can do this with a string, or with a Blob or File object as suits you.
This is documented in the MDN page for FormData.

Categories

Resources