Send multiple file inputs to the server without a form - ajax

With a form and the following input tag:
<input type="file" name="images" multiple="" class="inputFileHidden">
I can get the file images on my server just by simply searching through the request.files object:
images = request.files.getlist('images') // returns list of imgs.
In my case, I'm not working with a form, but rather I post it using AJAX.
With simple things like text I can easily post it as such:
Client side
const i_post_title = document.getElementsByName('post_title')[0];
....
$.post('/_upload_post', {
title_post: i_post_title.value
}).done(function(data){
console.log(data)
}).fail(function(){
console.log('fail')
})
And receive it on my server by accessing the request.form object and passing in the key of the payload sent from the client:
Server Side
title_post = request.form['title_post']
But with files, how should I handle it without having a form?

Related

How to avoid multipart/form-data with XMLHTTPRequest and FormData

I'm trying to setup an html form, the form action is an aws lambda function.
When I submit the form via plain html, all is well. However, when I'm sending the form via XMLHTTPRequest + FormData the lambda function breaks.
Looking at the lambda logs it seems that when using the plain html form send, the form is encoded in the request body as straight forward query string ('name=Johnny&email=john%40bon.com' etc) which my lambda function can parse.
However, when using XMLHTTPRequest+FormData to send the form, the form is encoded in using a different format which I believe (not sure) is called multipart/form-data (has these WebKitFormBoundaryXXX additions).
Is there a way to make XMLHTTPRequest+FormData send the form in the same format as is used when sending the form via plain html. Alternatively, how to do parse this multipart/form-data format in aws lambda python.
const form = document.querySelector('#my-form-id')
form.addEventListener('submit', event => {
// disable default action
event.preventDefault()
// configure a request
const xhr = new XMLHttpRequest()
xhr.open('POST', 'www.myurl.com/submit')
// prepare form data
let data = new FormData(form)
// set headers
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
// send request
xhr.send(data)
// listen for `load` event
xhr.onload = () => {
console.log(xhr.responseText)
}
})```
Is there a way to make XMLHTTPRequest+FormData send the form in the same format as is used when sending the form via plain html.
Well. It does… for a value of plain HTML with enctype='multipart/form-data'.
If you want to send application/x-www-form-urlencoded data then FormData is the wrong tool for the job.
Use URLSearchParams instead.
You can initialise it with a FormData object.
const data = new URLSearchParams(new FormData(document.querySelector('form')));
console.log(`${data}`);
<form>
<input name=foo value=bar>
<input name=x value=y>
</form>
Note that XMLHttpRequest can infer the Content-Type header when you pass a URLSearchParams or FormData object to send() so don't set it manually. That is just a waste of time and an opportunity to introduce errors.
Quentin's solution works. Here's the corrected code:
form.addEventListener('submit', event => {
// disable default action
event.preventDefault()
// configure a request
const xhr = new XMLHttpRequest()
xhr.open('POST', 'www.myurl.com/submit')
// send request
xhr.send(new URLSearchParams(new FormData(form)))
// listen for `load` event
xhr.onload = () => {
console.log(xhr.responseText)
}
})```

php post form max input vars strange behaviour

I am posting only 2 variables. If I do a direct POST using the form below it works.
<form action="http://someapi/post_html" method="post" enctype="multipart/form-data">
<input type="text" name="name" >
<textarea name="htmltemplate"> a html template of 3000 characters
</textarea>
<button type="submit">Submit</button>
</form>
When I use ajax to post the data I actually get a response back from the server max_input_vars limit of 1000 exceeded. How is it possible when I'm only sending 2 variables using ajax that I get that message?
I also tried using curl to do a POST and ended up receiving the same message.
$('form.ajax').on('submit',function() {
var formData = $('form.ajax').serialize();
formData += CKEDITOR.instances.textboxwyswygs.getData();
event.preventDefault();
$.ajax({
url: "http://someapi/post_html",
method:"POST",
data: formData,
success: function(response){
console.log(response);
}
})
});
Your formData looks broken - serialize() returns a JSON string and the CKEditor getData() returns a string of HTML.
Try this: synchronize the CKEditor value into the form before calling serialize and then your JSON will be correct. Try to console.log(formData) to check the formData before sending. So submitting the form without the syncrhonziation doesn't actually send the CKE content at all. This should be checked server-side to see what input it is getting.
Also the value of the ajax functions data member is expected to be correct JSON and servers might handle it in weird ways.
Other issues: $('form.ajax') does not actually target your HTML. Is this a correct example?
The event variable looks undefined, try naming it e and adding it as a parameter to .on('submit',function(e){..}.
You don't show the code where you actually replace the textarea with a CKEditor, it would be useful to see.

AJAX file upload in Play Framework 2.1 RC1 delivers an empty file

Scala/Play gurus out there.
I'm trying to upload a file using AJAX, in Play 2.1 (RC1). For the client part I'm using eldarion/bootstrap-ajax and everything seems to be fine, except that the uploaded file is empty.
The front-end snippet:
...
<form action="#routes.Campaigns.upload" method="post" class="form ajax replaceable" data-replace=".replaceable">
<input type="file" name="picture">
<p><input class="btn" type="submit"></p>
</form>
...
Note that I had to use the explicit <form> tag instead of the #form helper, due to the fact that the required css class (data-replace) contains a dash, and therefore can not be used as a Symbol. But anyway. The called action in the controller looks like this:
def upload = Action(parse.temporaryFile) {
request =>
Logger.info("Trying to upload a file")
val resultString = try {
val file = new File("/tmp/picture")
request.body.moveTo(file, true)
"file has been uploaded"
} catch {
case e: Exception => "an error has occurred while uploading the file"
}
val jsonResponse = Json.toJson(
Map("html" -> Json.toJson("<p>" + resultString + "</p>")
)
)
Ok(jsonResponse)
}
I'm aware that as my development goes forward the file name should be more intelligently set, but for the moment being, /tmp/picture is for me as good a name as any other one.
The JSON response gets generated (with the "file has been uploaded" message within), and is sent back to the browser as the payload of the 200 response. The JSON is received and correctly used to modify the page (in this case, merely removing the very uploading form).
But the file, although appearing in the right moment and in the right place, is always empty:
larsson:tmp bruno$ ls -l /tmp/picture
-rw-r--r-- 1 bruno staff 0 7 Jan 03:07 /tmp/picture
That's specially strange, in my opinion, because the uploading code which uses a traditional multipart/form-data form, with no AJAX whatsoever, and an Action with parse.multipartFormData as a parameter, instead of parse.temporaryFile, works finely.
Any help will be very appreciated. Thanks in advance.
I don't know bootstrap-ajax, anyway if it hasn't dedicated support for uploading files via AJAX (and I didn't find any info about that possibility in its readme file) it will NOT send files with AJAX.
Reason: In standard JavaScript uploading files with AJAX is not possible due the security limits and there are some techniques to workaround this, mainly using iFrames, however I can't see nothing similar in the code of bootstrap-ajax so probably you need to modify it or use other solution.
Solution: There are some AJAX file uploaders, which works good with HTML5 ie. jQuery File Upload, which offers ajax upload, multi-file uploads, drag file to the drop zone etc.
In general HTML5 supports file uploads better than earlier versions of HTML, so you can build uploader easily without need of using additional plugins, take a look to this topic. As you can see it delivers possibilities to validate some data BEFORE the upload and also offers progress bars.
I'm currently trying to implement something like this and I got a first version working. This is how I do it:
In my Controller I define a method for uploading files. In my case I use Action.async since I save stuff to my MongoDB with reactivemongo. I have removed that code so that it do not complicate this example.
What I do in this example is that I upload a csv file, save it to disk and then produce the first row back as a string to the user. In real life the method produces a list back so that user is able to choose which column represent what an so on.
I use mighty csv for csv parsing. GREAT LIB!
Application:
def upload = Action.async(parse.multipartFormData) {
implicit request =>
val result = uploadForm.bindFromRequest().fold(
errorForm => Future(BadRequest(views.html.index(errorForm))),
form => {
import java.io.File
request.body.file("csvFile").map {
csv =>
val path = current.configuration.getString("csv.job.new.file.path").getOrElse("")
val name = DateTime.now().getMillis + ".csv"
csv.ref.moveTo(new File(path + name))
val settings = CSVReaderSettings.Standard(linesToSkip = form.linesToSkip)
val rows: Iterator[Array[String]] = CSVReader(path + name)(settings)
val firstRow = rows.next()
val test = firstRow match {
case xs if xs.size == 0 || xs.size == 1 => xs.mkString
case xs if xs.size > 1 => xs.mkString(", ")
}
Future(Ok(test))
}.getOrElse(Future(BadRequest("ahadasda")))
}
)
result
}
routes:
POST /upload #controllers.Application.upload
I use # before the controllers because I use DI with guice for my service classes.
Since we will use javascript for uploading we need to define our jsRoutes:
jsRoutes:
def javascriptRoutes = Action {
implicit request =>
import routes.javascript._
Ok(
Routes.javascriptRouter("jsRoutes")(
Application.upload
)
).as("text/javascript")
}
Remember to import in your template where you want to use the routes:
<script type="text/javascript" src="#routes.Application.javascriptRoutes"></script>
<script src="#routes.Assets.at("javascripts/app.js")#Messages("js.version")" type="text/javascript" ></script>
In my view template I have a regular helper form. There is some css style stuff I do to
change the looks and feel of the upload button and file chooser. But the input fields
are there.
index.scala.html:
<div class="csvContainer">
#helper.form(action = routes.Application.upload, 'enctype -> "multipart/form-data", 'id -> "csvUpload") {
#Messages("upload.row.skip")
#inputText(uploadForm("linesToSkip"), 'class -> "hidden")
<div style="position:relative;">
<div id="csvFile" style="position:absolute;">
#Messages("upload.choose")
</div>
<input id="uploadFile" type="file" name="csvFile" style="opacity:0; z-index:1;" onchange="document.getElementById('csvFile').innerHTML = this.value;" />
</div>
<p>
<input type="submit" value="#Messages("upload.submit")">
</p>
}
</div>
In app.js is where the ajax magic happens, remember I have not implemented any validation or cool html5 stuff yet as the progressbar and other handlers, described in besiors link.
I use regular JQuery.
app.js:
$('#uploadFile').change(function(){
var name = $(this).val().split("\\");
console.log(name[2]);
$('#csvFile').text(name[2]);
});
$('#csvFile').click(function(){
$('#uploadFile').click();
});
$("#csvUpload").submit(function(e) {
e.preventDefault();
var formData = new FormData();
formData.append('csvFile', $( '#uploadFile' )[0].files[0]);
formData.append('linesToSkip', $( "#linesToSkip").val());
jsRoutes.controllers.Application.upload().ajax({
data: formData,
processData: false,
contentType: false,
cache: false,
type: 'POST',
success: function(data){
alert(data);
}
});
});
I have removed a lot of code to simplify this example and I hope that I have not forgotten anything. Hope this helps!

How do I send arbitrary JSON to node.js without a page reload?

My ultimate goal is to send an arbitrary JSON to node.js when a button is clicked. I currently only know how to send input from a form. Here's some code I put together to send form information:
function postForm() {
$('form').submit(function(e) {
e.preventDefault(); // no page reload
$.post(
$(this).attr('action'),
$(this).serialize(),
function(data) { console.log('Code for handling response here.') },
'json'
);
});
}
Where the HTML looks like:
<form action='/send' method='post'>
<input name= "foo" type="radio" value=1>
<input type="submit" value="Submit">
</form>
And the relevant express/node.js code looks like:
app.post('/send', function(request, response) {
fs.appendFile('test.txt', JSON.stringify(request.body) + '\n', function(e) {
if(e) throw e;
console.log(request.body);
});
});
However, I don't know how to adapt this example to use data that is not from form input. To give context, I'm building a web-based user study, and I want to send various information collected about the user to node.js. I've tried variants of what was working for the form submission, but none of my attempts have been successful. My impression was that I could just swap out $(this).serialize() to any other data that the client can access, but I couldn't get this line of thought to work. I also tried altering some of the many .ajax() examples, but those always redirected the page which is undesirable, since my study will lose user-state information if the page refreshes.
I've done decent amount of client and server side programming, but I have next to no knowledge about how ajax works, which is proving rather problematic for solving this! And also rather silly since, often times, that's what glues the two together :)
Since you're using jQuery, sending data is simple – call $.post(url, data) from the button's click handler:
$('#somebutton').click(function() {
var data = { key: 'value', ... };
$.post('/send', data, function(res) {
// success callback
});
});
The browser will POST to url with a URL-encoded serialization of the data argument.
POST /send HTTP/1.1
Content-Type: application/x-www-form-urlencoded
...
key=value&...
Which Express' bodyParser will have no trouble with. Alternatively, you can tell jQuery to send a JSON serialization of data:
$.post('/send', data, function(res) {}, 'json');
In your case, it really doesn't matter how jQuery transmits the data (URL encoded or JSON), since bodyParser automatically deserializes both formats.

Uploading Image Using JQuery And Django

Before you continue reading, trust me when I say I have read all the other posts on this subject, and none of them helped.
I am trying to add image upload functionality to my website. I want to upload the image
via an ajax post. I cannot get this working.
Here is what I have:
HTML - i have a special setup so that an image is displayed instead of a stupid button
and the text field. I am also using the onChange event to automatically submit when I have hit "OK" after selecting the image.
<form id="add-picture-form" method="POST" action="/api/upload_image/" enctype="multipart/form-data">{% csrf_token %}
<div class="thumbnails" style="width:400px;">
<label class="cabinet BrandHeader">
<input type="file" class="file" id="upload-photo" onChange="$('#add-picture-form').submit();" />
</label>
</div>
</form>
Jquery:
$('#add-picture-form').submit(function() {
//var filename = $("#upload-photo").val();
var photo = document.getElementById("upload-photo");
var file = photo.files[0];
$.ajax({
type: "POST",
url: "/api/upload_image/",
enctype: 'multipart/form-data',
data: {'file': file.getAsBinary(), 'fname' : file.fileName },
success: function(){
alert( "Data Uploaded: ");
}
});
return false;
});
Finally my django view that is hit when you post to /api/upload_image/
def ajax_upload( request ):
print request.POST
print request.FILES
return http.HttpResponse(simplejson.dumps([True]), mimetype='application/javascript')
I have tried to write the image to binary, but I cannot open that data that has written.
Why is uploading an image using javascript so hard? I am an idiot and just not using a simple solution? If so, please tell me what is the best way to use jQuery to upload an image in Django.
Try the jQuery plugins Uploadify or SWFUpload. Someone even did the Django integration for you, see: https://github.com/tstone/django-uploadify and http://blog.fogtunes.com/2009/11/howto-integrate-swfupload-with-django/.
I'm not that familiar with django but I think the issue is that uploading a file via AJAX isn't as simple as you might think.
There are several methods of getting around this, but I recommend using one that already exists. Since you are using jquery, I would recommend the jquery forms plugin: http://jquery.malsup.com/form/#getting-started
The plugin supports file uploading out of the box, and really all you'll need to do is wire it up to your form:
$('#add-picture-form').ajaxForm();
see also: How can I upload files asynchronously?

Resources