multipart/formdata is not sending file data with jQuery.ajax - javascript

I have an endpoint from our Django backend guys with documentation that reads:
POST to /api/1/photo-uploads/ with enctype="multipart/form-data" with files in field called "files[]".
I've been attempting to send uploaded files with formData using jquery's AJAX method. I continue to get an error indicating that the file was not sent. When I view the payload I see.
undefined
------WebKitFormBoundary9AzM2HQPcyWLAgyR
Content-Disposition: form-data; name="file"; filename="auzLyrW.jpg"
Content-Type: image/jpeg
Which doesn't necessarily mean that it hasn't sent but there certainly isn't a location being posted. And I don't have any kind of verification that the file is uploaded.
var formData = new FormData();
formData.append('file', $('#file-upload').get(0).files[0]);
$.ajax({
url: '/api/1/photo-uploads/',
type: 'POST',
data: formData,
cache: false,
contentType: false,
processData: false,
});
When I console.log formData it simply show's the prototype methods like .append. So I'm unable to verify if the file's data is being sent beyond checking the payload. I can log $('#file-upload').get(0).files[0] but I only see details from the file itself. Because I'm testing it locally an upload location should be something like localhost:8000/.
The backend guys are under the impression that it's something I'm doing. When I do a simple form post it works fine. I've tried a number of plugins and basic methods and all have produced the 400 {"message": "No photos supplied.", "success": false}
Any ideas would be appreciated.

The documentation asked that it be called files[]. What was being sent was file.
formData.append('files[]', $('#file-upload').get(0).files[0]);

Related

Prevent Javascript adding metadata on File Upload to S3

Basically I am willing to upload files directly to S3 via browser i.e without any web server acting as a middle-ware or proxy like this.
So I am generating pre-signed URL using boto3 library like this:
def put_url(self, key):
url = self.client.generate_presigned_url(
ClientMethod="put_object",
Params={
"Bucket": "visweswaran",
"Key": key
}
)
return url
and this returns a pre-signed URL which is completely fine. I am using JQuery to make ajax PUT request to the S3 to upload my file.
let file_data = document.getElementById("file_data").files[0];
var form = new FormData();
form.append("", file_data, "test.txt");
var settings = {
"url": url,
"method": "PUT",
"timeout": 0,
"processData": false,
"mimeType": "multipart/form-data",
"contentType": "text/plain",
"beforeSend": function(xhr){xhr.setRequestHeader('Content-Disposition', 'attachment');},
"data": form
};
$.ajax(settings).done(function (response) {
location.reload();
});
The file gets uploaded to the S3 successfully via browser. But when I open the file I see strange meta data getting added to the top of the file like this,
-----------------------------33057860671031084693134041830 Content-Disposition: form-data; name="name"
test.txt
-----------------------------33057860671031084693134041830 Content-Disposition: form-data; name="file"; filename="test.txt"
Content-Type: text/plain
I have also tried a more formal solution like Pluploader (https://www.plupload.com/) and I am facing the same problem. I would like somebody to point me in the right direction to fix it. Ant help is much appreciated.
References:
https://softwareontheroad.com/aws-s3-secure-direct-upload/
How to upload to AWS S3 directly from browser using a pre-signed URL instead of credentials?
Working Solution
I have tested with a video and you don't need a form. Just send the data directly
let video = document.getElementById("video_file").files[0];
var settings = {
"url": url,
"method": "PUT",
"timeout": 0,
"processData": false,
"data": video
};
$.ajax(settings).done(function (response) {
location.reload();
});
I have tried uploading a txt file with a presigned-put-url using two approaches:
Sending A form data: (this is used with POST urls not PUT)
This actually add the content-disposition header to the final file as mentioned in the question.
Sending raw binary data (recommended way and this how PUT url is used!):
The file was uploaded correctly, and does not include the content-disposition header.
Could you try sending the PUT request without using formData at all?
The ajax's data attribute should have a value of file_data, and the content-type while the signing the S3 URL and sending (ajax) should be ContentType: 'binary/octet-stream'.
If you need to use formData, check out S3's preSignedPost.

Django request.FILES gets name, filename but not file (blob)

I'm trying to send an audiofile via a POST request to the server (aws, ec2) and I'm using Django, but my request.FILES doesn't receive the blob file, but it DOES receive the key and the filename.
Everything worked fine when I ran this on localhost.
How can I get the file?
I'm enabling my website on chrome://flags/#unsafely-treat-insecure-origin-as-secure, so that I can access the microphone.
Using RecorderJs to generate a Blob object containing the recorded audio in WAV format.
Main.js
rec.exportWAV(function(blob){
...
var fd = new FormData();
fd.append('text', speech);
fd.append('audio', blob, 'test.wav');
$.ajax({
type: 'POST',
enctype: 'multipart/form-data',
url: url,
data: fd,
processData: false,
contentType: false,
success: function(response) {
console.log(response);
}
})
...
speech is String,
blob in console is Blob {size: 221228, type: "audio/wav"}, so it does exist.
Views.py:
#csrf_exempt
def get_blob(request):
thislist = []
for key in request.POST:
thislist.append(request.POST.get(key))
for key in request.FILES:
thislist.append(request.FILES.get(key).name)
json_stuff = json.dumps({"check": thislist})
return HttpResponse(json_stuff, content_type="application/json")
I've tried with and without enctype, doesn't make a difference.
I've tried setting contentType to multipart/form-data, doesn't make a difference.
The formdata seems to be sent correctly, because I can get the speech correctly (request.POST).
And I can get the key from request.FILES ('audio'), and get the filename ('test.wav').
If I try request.FILES['audio'].read(), it says MultiValueDictError.
If I try request.FILES.get('audio').read() it says AttributeError, 'NoneType' object has no attribute 'read'.
When I print request.POST I do get the dictionary with 'text': whatever text I've spoken.
When I print request.FILES I get an empty dictionary even though I can get the key and filename through
for key in request.FILES: and request.FILES['audio'].filename.
Does anyone know what's going on and/or can help me with the problem?
You may use read() method for it:
#csrf_exempt ## it doesn't work for post requests in later Django versions, so you need to disable it in another way or add a token to all post requests
def get_blob(request):
thislist = []
for key in request.POST:
thislist.append(request.POST.get(key))
for key in request.FILES:
thislist.append(request.FILES.get(key).name)
# I am not sure that "name" exists in the request — you may use any filename, although the following works with multipart requests.
with open(request.FILES["name"],"wb+") as f:
f.write(request.FILES['file'].read())
json_stuff = json.dumps({"check": thislist})
return HttpResponse(json_stuff, content_type="application/json")
By the way,
#csrf_exempt — doesn't work for post requests in later Django versions, as a token is checked before your view is even called. So, you may need to disable CSRF middleware or just add a correct 'X-CSRFToken' token to all requests.

JQuery ajax POST with FormData doesn't send any values

There seem to be lots of posts about this, but can't find a solution amongst them and I've hit a brick wall. Help, dear Stackers!
I'm writing a bit of code that lets users choose a profile picture, crop it and then save the cropped image on the server. I'm using the Cropper.js library to handle the actual cropping and have established that's working because I can display the crop live with this code:
var imageData = cropper.getCroppedCanvas().toDataURL();
var profileimage = document.getElementById('myProfilePhoto');
profileimage.src = imageData;
Now I want to send the cropped image to the server, and the best method would appear to be create a blob and Ajax that over to a php script to handle it.
cropper.getCroppedCanvas().toBlob(function (blob) {
var formData = new FormData();
formData.append('file', blob);
formData.append('uuid', '{a unique identifier}');
$.ajax({
type: 'POST',
url: '{the php script}',
data: formData,
processData: false,
contentType: false,
success: function (response) {
console.log('Upload success: '+response);
},
error: function () {
console.log('Upload error');
}
});
});
And at this stage I'm just getting my php script to var_dump($_REQUEST) so I can see in the console what the script thinks it's getting. However, the response is just sending back the default $_REQUEST objects like PHPSESSID and no file or uuid input.
What am I doing wrong?
Thanks in advance for any pointers.
If you are sending via post in order to see the request put this code
var formData = new FormData();
formData.append('file', blob);
formData.append('uuid', '{a unique identifier}');
formData.append('_method', 'PATCH'); <---add this and you're good to go
So the answer was annoyingly simple but frustratingly hard to find.
My server had an .htaccess rewrite rule which automatically changed www. addresses to the non-www version, and obviously I'd been dumb enough to include the www. in the URL I was calling, and it seems that's enough to strip off the $_POST data in these circumstances.
So FormData() and blobs are all red herrings here - it's just a redirect that's the cause.

How to capture file downloaded as a result of cross domain post request?

I can not use Ajax because I do not have access to the third party server
File download starts automatically after submitting the form
I want to import this file (it's a CSV) into an array, but for that I need to somehow capture it
All examples that I researched are showing capture of a response to an AJAX call, which I can not use due to CORS.
Current code includes a post form, and a submit function.
This does not work due to CORS constraints:
$('#f1')
.ajaxForm({
url : $('#f1').attr('action'),
type: 'post',
dataType : 'document',
crossDomain: true,
success : function (response) {
alert("The server says: " + response);
}
})
This of course works but file is downloaded to the filesystem:
$('#f1').submit()
I need to somehow extract the attachment from the post reponse and load it into a variable.

How to build a file from binary string using javascript

I am trying to use BusinessObject RESTful API to download a generated (pdf or xls) document.
I am using the following request:
$.ajax({
url: server + "/biprws/raylight/v1/documents/" + documentId,
type: "GET",
contentType: "application/xml",
dataType: "text",
headers: {"X-SAP-LogonToken": token, "Accept": "application/pdf" },
success: function(mypdf) {
// some content to execute
}
});
I receive this data as a response:
%PDF-1.7
%äãÏÒ
5 0 obj
<</Length 6 0 R/Filter/FlateDecode>>
//data
//data
//data
%%EOF
I first assumed that it was a base64 content, so in order to allow the users to download the file, I added these lines in the success function:
var uriContent = "data:application/pdf; base64," + encodeURIComponent(mypdf);
var newWindow=window.open(uriContent, 'generated');
But all I have is an ERR_INVALID_URL, or a failure while opening the generated file when I remove "base64" from the uriContent.
Does anyone have any idea how I could use data response? I went here but it wasn't helful.
Thank you!
. bjorge .
Nothing much can be done from client-side i.e. JavaScript.
The server side coding has to be changed so that a url link is generated (pointing to the pdf file) and sent as part of the response. The user can download the pdf from the url link.
You cannot create file using javascript, JavaScript doesn't have access to writing files as this would be a huge security risk to say the least.
To achieve your functionality, you can implement click event which target to your required file and it will ask about save that file to user.

Categories