Inconsistent AJAX POST status 400 . Issues with image complexity - ajax

Our team has developed a JS HTML5 canvas based paint application. In the following code, the image data is fetched from the canvas as base 64 encoding and posted to a servlet via ajax. The data post behaves erratically. If the image is simple , as in a straight line, I get Ajax status = 200 and the image gets saved. If the image is complex, then I get a status = 400 and the data is not saved.
Why should the content of the POST create issues with posting of the data itself?
function getCode(){
var canvas = document.getElementById('imageView');
var context = canvas.getContext('2d');
// draw cloud
context.beginPath();
// save canvas image as data url
var dataURL = canvas.toDataURL();
// set canvasImg image src to dataURL
// so it can be saved as an image
document.getElementById('canvasImg').src = dataURL;
var uri= document.getElementById('canvasImg').src;
uri = uri.replace('data:image/png;base64,','');
uri = uri.replace('=', '');
uri = uri.trim();
alert("uri is "+uri);
var ajaxobject ;
if(window.XMLHttpRequest){
ajaxobject = new XMLHttpRequest();
} else if(window.ActiveXObject){
ajaxobject = new ActiveXObject("Microsoft.XMLHTTP");
}else if(window.ActiveXObject){
ajaxobject = new ActiveXObject("Msxml2.XMLHTTP");
}
ajaxobject.open("POST", "SaveImageServlet?image="+uri, true);
ajaxobject.setRequestHeader("Content-type","application/x-www-form-urlencoded");
ajaxobject.onreadystatechange = function(){
if(ajaxobject.readyState==4){
alert(ajaxobject.status);
if(ajaxobject.status==200){
alert(ajaxobject.responseText);
}}
};
ajaxobject.send(null);
}

From looking at your code, the problem seems that you're passing the data in querystring instead of using the request body (as you should be doing since you're setting the POST verb).
Your uri should look like this:
SaveImageServlet
without the question mark and the parameter. The parameter should be set in the request body. Using jquery ajax your request would look like this:
$.ajax({
contentType: 'text/plain',
data: {
"image": yourBase64string
},
dataType: 'application/json', // or whatever return dataType you want
success: function(data){
// callback in case of success
},
error: function(){
// callback in case of error
},
type: 'POST',
url: '/SaveImageServlet'
});
On server side you should be reading the data from the appropriate place. For example, if you're using .Net read it like this:
Request.Form["image"]
instead of:
Request.Querystring["image"]
This should work as intended and consistently.

#Matteo, Thanks for your help and effort. However, AJAX issue never got solved. I found a way to send the base64 image data to the servlet. Just appended it to a hidden field and sent it as a regular form field.

Related

How to download the model as a JSON file?

My model is held in a JavaScript object on the client side, where the user can edit its properties via the UI controls. I want to offer the user an option to download a JSON file representing the model they're editing. I'm using MVC core with .net 6.
What I've tried
Action method (using Newtonsoft.Json to serialize the model to JSON):
public IActionResult Download([FromForm]SomeModel someModel)
{
var json = JsonConvert.SerializeObject(someModel);
var characters = json.ToCharArray();
var bytes = new byte[characters.Length];
for (var i = 0; i < characters.Length; i++)
{
bytes[i] = (byte)characters[i];
}
var stream = new MemoryStream();
stream.Write(bytes);
stream.Position = 0;
return this.File(stream, "APPLICATION/octet-stream", "someFile.json");
}
Code in the view to call this method:
<button class="btn btn-primary" onclick="download()">Download</button>
And the event handler for this button (using jQuery's ajax magic):
function download() {
$.ajax({
url: 'https://hostname/ControllerName/Download',
method: 'POST',
data: { someModel: someModel },
success: function (data) {
console.log('downloading', data);
},
});
}
What happened
The browser console shows that my model has been posted to the server, serialized to JSON and the JSON has been returned to the browser. However no file is downloaded.
Something else I tried
I also tried a link like this to call the action method:
#Html.ActionLink("Download", "Download", "ControllerName")
What happened
This time a file was downloaded, however, because ActionLink can only make GET requests, which have no request body, the user's model isn't passed to the server and instead the file which is downloaded represents a default instance of SomeModel.
The ask
So I know I can post my model to the server, serialize it to JSON and return that JSON to the client, and I know I can get the browser to download a JSON-serialized version of a model, but how can I do both in the same request?
Edit: What I've done with the answer
I've accepted Xinran Shen's answer, because it works as-is, but because I believe that just copying code from Stack Overflow without understanding what it does or why isn't good practice, I did a bit of digging and my version of the saveData function now looks like this:
function saveData(data, fileName) {
// Convert the data to a JSON string and store it in a blob, a file-like
// object which can be downloaded without it existing on the server.
// See https://developer.mozilla.org/en-US/docs/Web/API/Blob
var json = JSON.stringify(data);
var blob = new Blob([json], { type: "octet/stream" });
// Create a URL from which the blob can be downloaded - see
// https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
var url = window.URL.createObjectURL(blob);
// Add a hidden hyperlink to the page, which will download the file when clicked
var a = document.createElement("a");
a.style = "display: none";
a.href = url;
a.download = fileName;
document.body.appendChild(a);
// Trigger the click event on the hyperlink to download the file
a.click();
// Release the blob's URL.
// Browsers do this when the page is unloaded, but it's good practice to
// do so as soon as it's no longer needed.
window.URL.revokeObjectURL(url);
// Remove the hidden hyperlink from the page
a.remove();
}
Hope someone finds this useful
First, Your code is right, You can try to access this method without ajax, You will find it can download file successfully,But You can't use ajax to achieve this, because JavaScript cannot interact with disk, you need to use Blob to save the file. change your javascript like this:
function download() {
$.ajax({
url: 'https://hostname/ControllerName/Download',
method: 'Post',
data: { someModel: someModel },,
success: function (data) {
fileName = "my-download.json";
saveData(data,fileName)
},
});
}
var saveData = (function () {
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
return function (data, fileName) {
var json = JSON.stringify(data),
blob = new Blob([json], {type: "octet/stream"}),
url = window.URL.createObjectURL(blob);
a.href = url;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
};
}());
I think you may need FileStreamResult, also you need to set the MIME type to text file or json file.
// instead of this
return this.File(stream, "APPLICATION/octet-stream", "someFile.json");
// try this
return new FileStreamResult(stream, new MediaTypeHeaderValue("text/plain"))
{
FileDownloadName = "someFile.txt"
};
// or
return new FileStreamResult(stream, new MediaTypeHeaderValue("application/json"))
{
FileDownloadName = "someFile.json"
};
Reference: https://www.c-sharpcorner.com/article/fileresult-in-asp-net-core-mvc2/

Django - Making an Ajax request

Im having a hard time figuring out how to integrate this ajax request into my view. I'm still learning how to integrate django with ajax requests.
My first question would be: Does the ajax request need to have its own dedicated URL?
In my case I am trying to call it on a button to preform a filter(Preforms a query dependent on what is selected in the template). I have implemented this using just django but it needs to make new request everytime the user preforms a filter which I know is not efficient.
I wrote the most basic function using JQuery to make sure the communication is there. Whenever the user changed the option in the select box it would print the value to the console. As you will see below in the view, I would to call the ajax request inside this view function, if this is possible or the correct way of doing it.
JQuery - Updated
$("#temp").change( function(event) {
var filtered = $(this).val();
console.log($(this).val());
$.ajax({
url : "http://127.0.0.1:8000/req/ajax/",
type : "GET",
data : {
'filtered': filtered
},
dataType: 'json',
success: function(data){
console.log(data)
},
error: function(xhr, errmsg, err){
console.log("error")
console.log(error_data)
}
});
Views.py
def pending_action(request):
requisition_status = ['All', 'Created', 'For Assistance', 'Assistance Complete', 'Assistance Rejected']
FA_status = RequisitionStatus.objects.get(status='For Assistance')
current_status = 'All'
status_list = []
all_status = RequisitionStatus.objects.all()
status_list = [status.status for status in all_status]
# This is where I am handling the filtering currently
if request.GET.get('Filter') in status_list:
user_req_lines_incomplete = RequisitionLine.objects.filter(Q(parent_req__username=request.user) & Q(status__status=request.GET.get('Filter')))
current_status = request.GET.get('Filter')
else:
user_req_lines_incomplete = RequisitionLine.objects.filter(parent_req__username=request.user).exclude(status__status='Completed')
user_reqs = Requisition.objects.filter(par_req_line__in=user_req_lines_incomplete).annotate(aggregated_price=Sum('par_req_line__total_price'),
header_status=Max('par_req_line__status__rating'))
return render(request, 'req/pending_action.html', { 'user_reqs':user_reqs,
'user_req_lines_incomplete':user_req_lines_incomplete,
'requisition_status':requisition_status,
'current_status':current_status,
'FA_status':FA_status})
def filter_status(request):
status = request.GET.get('Filter')
data = {
'filtered': RequisitionLine.objects.filter(Q(parent_req__username=request.user) & Q(status__status=status)),
'current_status': status
}
return JsonResponse(data)
Urls.py
path('pending/', views.pending_action, name='pending_action')
First: you have to divide your template to unchangeable part and the part that you want to modify with your filter.
Second: for your goal you can use render_to_string. See the followning link https://docs.djangoproject.com/en/2.1/topics/templates/#usage
code example (views.py):
cont = {
'request': request, #important key-value
'your_models_instances': your_models_instances
}
html = render_to_string('your_filter_template.html', cont)
return_dict = {'html': html}
return JsonResponse(return_dict)
In your js file you need to determine relative url "{% url 'name in yours url file'%}"
And in success you need to add next line:
success: function(data){
$(".filter-block").html(data.html);
}
i hope it will help you! Good luck!

Asynchronous form-data POST request with xmlhhtprequest

I am trying to upload a file to the REST Api of Octoprint, which should be done by sending a POST request with Content-Type: multipart/form-data
(http://docs.octoprint.org/en/master/api/fileops.html#upload-file)
I am using NodeJS and two libraries, XmlHttpRequest and form-data. When trying:
var xhr = new xmlhttprequest() ;
var form = new formData() ;
form.append('exampleKey', 'exampleValue');
xhr.open("POST","octopi.local/api/local", true) ;
xhr.setRequestHeader("Content-Type","multipart/form-data") ;
xhr.send(form) ;
I get an error at the xhr.send line :
TypeError: first argument must be a string or Buffer
If I make a synchronous request by using xhr.open("POST",url,false), this error disappears.
Why is it so ? Is there a way to turn it into an asynchronous request ?
EDIT Actually, I don't really understand the documentation. I suppose that I should set the file I want to upload by using form.append("filename", filepath, "exampleName"), but I am not sure about that. The fact is that I noticed that I get the TypeError even if I try a simplified request, without sending any file.
EDIT2 This is the modified code, which returns the same error :
var XMLHttpRequest=require('xmlhttprequest').XMLHttpRequest ;
var FormData = require('form-data');
var data = new FormData();
data.append("key","value" );
var xhr = new XMLHttpRequest();
xhr.open('POST', "octopi.local/api/files/");
xhr.send(data);
After a long time working on this, I finally managed to upload a file. If you use NodeJS, don't rely on the MDN documentation: it tells what the libraries should do, not what they can actually do on the node platform. You should only focus on the docs available on GitHub.
It seems that it is not currently possible to send a form with XMLHttpRequest : I tried using JSON.stringify(form) but then wireshark tells me that the request is not a multipart/formdata request.
If you want to upload a file, you should rather use the 'request' module. The following has worked for me :
exports.unwrappeduploadToOctoprint = function(){
"use strict" ;
var form ={
file: {
value: fs.readFileSync(__dirname+'/test2.gcode'),
options: { filename: 'test2.gcode'}
}
};
var options = {
method: 'POST',
url: 'http://192.168.1.24/api/files/local',
headers: { 'x-api-key': 'E0A2518FB11B40F595FC0068192A1AB3'},
formData: form
};
var req = request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(body);
});
};
Seems that you have some typos in your code. Use code snippet below instead. Replace the relevant parts according to your needs
var fileToUpload = document.getElementById('input').files[0];
var data = new FormData();
data.append("myfile", fileToUpload);
var xhr = new XMLHttpRequest();
xhr.open('POST', "upload_endpoint");
xhr.send(data);

Meteor call for sending image through api

Is it is possible to send image through meteor call to the API?
Client js
var r = {image : image};
Meteor.apply('callToServer', r, function(error, result){
console.log(result);
});
Server js
Meteor.methods({
uploadAndSaveToDB: function(data){
var result = Meteor.http.post(apiUrl, {
params: { image : data['image']}
});
var result = JSON.parse(result.content);
return result;
},
});
If your question is about how to get the image data and send it to your api, it depends on a couple factors:
How are you getting the image's data in the first place from your app (a submission form, a URL, some drawing library...)
In what format does the API you are calling expects the image data to be sent (URL, raw data, encrypted...)
If you are simply asking if it is doable, then yes, definitely. You will just need to add the http package for this:
meteor add http
You can then make requests to your api pretty much like you wrote it. Just make sure to give the right name to your method call (also use call and not apply if you are not submitting an array of arguments):
Client js
var r = {image : image};
Meteor.call('uploadAndSaveToDB', r, function(error, result){
console.log(result);
});
Server js
Meteor.methods({
uploadAndSaveToDB: function(data){
var result = HTTP.post(apiUrl, {
params: { image : data['image']}
});
var result = JSON.parse(result.content);
return result;
},
});

How I can catch and save to file data from form sended by AJAX in ColdFusion

I have the followign JavaScript code:
function upload(blob) {
var xhr = new XMLHttpRequest();
var url = "test.cfm";
xhr.onload=function(e) {
if(this.readyState === 4) {
console.log("Server returned: ",e.target.responseText);
}
};
var fd=new FormData();
fd.append("randomname",blob);
xhr.open("POST",url,true);
xhr.send(fd); }
How can I catch it on server side by ColdFusion and Save blob object to File?
Can someone please some code sample. Thx.
PS. I am pretty new in CF.
Since you are using formdata, you can access the form variable with ajax, just like you would with normal http requests.
#form.randomname#
#form['randomname']#
So you could save the content in a file with
<cfscript>
fileWrite( 'c:\myfile.txt', form.randomname );
</cfscript>

Resources