Sending byte array over ajax [closed] - javascript

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
I am having ajax request which is working only half way.
function receivedText() {
alert(fr.result); //Here i have good result
$.ajax({
type: "POST",
url: "/Gallery/UploadImage",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: {
byteArray: fr.result,
fileName: $('input[type=file]').val().split('\\').pop()
},
success: function (data) {
if (data == 0)
alert("error");
else
alert("Success");
},
error: function () {
alert("ERROR");
}
});
}
Here is my request. As you can see i commented up there that in my test(alert) fr.result has value BUT when i debug and go see it in my controller, it is NULL.
Here is my controller.
[HttpPost]
public IActionResult UploadImage(byte[] byteArray, string fileName)
{
try
{
System.IO.File.WriteAllBytes(_hostingEnvironment.WebRootPath + "\\Uploads\\Images\\" + fileName, byteArray);
return Json(0);
}
catch
{
return Json(0);
}
}

Your're using ajax in a wrong way.
The first error is a mismatch with Content-Type
$.ajax({
...
contentType: "application/json; charset=utf-8",
...
data: {
byteArray: fr.result,
fileName: $('input[type=file]').val().split('\\').pop()
},
...
}
Although you've set the Content-Type=application/json, the payload sent to server will be form-url-encoded by default:
fileName=Xyz&byteArray=
If you need JSON format, you should use JSON.stringify({...}) to get a text representation.
The contentType: "application/json; is not suitable here. That's because :
The JSON is not designed to deal with binary data but used for text. You can't send a byte[] with json.
The server side code expects simple type from query/routes/form. If you need json, they should be something like IActionResult UploadImage([FromBody] Fr fr)
If you're sending an image, the easiest way is to use the Content-Type of multipart/form-data with the IFormFile on the server side at the same time.
// action method
public IActionResult UploadImage(IFormFile image, string fileName)
{
// ...
}
and now you could send a FormData :
// your receivedText() function
function receivedText(){
var formData = new FormData();
formData.append('fileName', 'Xyz.img');
// if you need upload image
var inputFileElement=document.getElementById("inputFileImage");
formData.append('image', inputFileElement.files[0]);
// of if you're already have a `byte[]`, you could do it as below:
// var blob = new Blob([bytearray]...); // see https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob
// formData.append('image', blob);
$.ajax({
type: "POST",
url: "/Gallery/UploadImage",
contentType: false,
processData: false,
data: formData,
success: function (data) {
console.log(data);
// ...
},
error: function () {
// ...
}
});
}

That would be your idea:
public class UploadImageBindings {
public string byteArray {get;set;}
public string fileName {get;set;}
}
[HttpPost]
public IActionResult UploadImage(UploadImageBindings bindings)
{
try
{
var bytes = System.Text.Encoding.UTF8.GetBytes(bindings.byteArray);
System.IO.File.WriteAllBytes(_hostingEnvironment.WebRootPath + "\\Uploads\\Images\\" + bindings.fileName, bytes);
return Json(0);
}
catch
{
return Json(0);
}
}
Your problem is that you not post as byte[] but you have to post as string !

Related

Ajax javascript Map to Spring controller

I am trying ajax Javascript Map, to spring controller. but it's getting null in backend. Please excuse if i am repeating question.
I can't change Map type, as my whole front end logic on it. Because set, get and has method from Map is what I need.
var ansMap = new Map(); // This way i created object
// added many values in ansMap,
$.ajax({
type: "POST",
url: myUrl,
contentType : 'application/json',
//cache: false,
dataType : 'json',
data : ansMap, // can't JSON.stringy(ansMap) as it gives empty json
success: function(result) {
console.log(result);
},
Spring code
#RequestMapping (value="myUrl", method=RequestMethod.POST)
public #ResponseBody String saveData(#RequestParam(required = false, value = "mapData") Map<String,List<String>> mapData, Model map)
{
log.info("Call Success");
log.info("mapData: "+mapData);
Please suggest what needs to be done here.
You can actually send your Map without mutating the value
var ansMap = new Map(); // This way i created object
// added many values in ansMap,
$.ajax({
type: "POST",
url: myUrl,
contentType : 'application/json',
//cache: false,
dataType : 'json',
data : JSON.stringify(Object.fromEntries(ansMap)), // can't JSON.stringy(ansMap) as it gives empty json
success: function(result) {
console.log(result);
},
That will turn it into a javascript object.
Object.fromEntries Will turn you Map into a javascript object, without altering the original Map
Regarding your backend i think you mis-interpret the #RequestParam annotation
The #RequestParam is to extract query parameters, form parameters and even files from the request.
I think that what you are looking for is #RequestBody.
Meaning you would be looking for something similar to :
#RequestMapping(value="/myUrl",method = RequestMethod.POST)
public String saveData( #RequestBody Map<String,Object> body) {
This should work
page
<button id="doPost"> post </button>
<script>
$(function () {
var map = new Map();
map.set('CIQ_2','aa');
map.set('CIQ_3','78965412300');
console.log(map);
$("#doPost").click (function() {
var settings = {
beforeSend: function(xhr, options) {
xhr.setRequestHeader("content-type" ,"application/json; charset=utf-8");
},
type: 'POST',
url: '/post' ,
data: JSON.stringify(Object.fromEntries(map))
}
$.ajax(settings).done(function(result) {
console.log("done : " + result);
});
});
});
</script>
Controller
#PostMapping("/post")
#ResponseBody
public String post(#RequestBody Map<String,String> data) {
System.out.println(data);
return "data well received";
}
will print
{CIQ_2=aa, CIQ_3=78965412300}
working code on GitHub

JQuery/Ajax & Spring Rest Multi-part form submit

I am quite new to JQuery and I was trying to do some asynchronous multipart form uploading. The form consist of few data fields and a file type. I have set up the server side code (Spring) like this:
#RequestMapping(method = RequestMethod.POST)
public #ResponseBody
Upload multipleSave(MultipartHttpServletRequest request)
{
Upload upload = new Upload();
Iterator<String> iterator = request.getFileNames();
while (iterator.hasNext())
{
MultipartFile file = request.getFile(iterator.next());
try
{
System.out.println(MessageFormat.format("File Length: {0}", Arrays.toString(file.getBytes())));
System.out.println("File Type: " + file.getContentType());
upload.setContent(file.getBytes());
upload.setDocId(id++);
upload.setError(null);
upload.setName(file.getName());
upload.setSize(file.getSize());
fileList.put(upload.getDocId().toString(), upload);
} catch (Exception e)
{
System.out.println("Error occurred: " + e);
upload.setError("500: Something went wrong!");
}
}
return upload;
}
and client side code like this:
function processFileUpload()
{
console.log("fileupload clicked");
var formData = new FormData();
formData.append("file", files[0]);
$.ajax({dataType: 'json',
url: "/SpringJqueryFileUpload/upload",
data: formData,
type: "POST",
enctype: 'multipart/form-data',
processData: false,
contentType: false,
success: function (result) {
alert('success' + JSON.stringify(result));
},
error: function (result) {
alert('error' + JSON.stringify(result));
}
});
}
When I do submit, the server responds with this:
java.lang.IllegalArgumentException: No converter found for return value of type: class com.upload.model.Upload
I am wondering with error. Could I be missing something here??
I would try changing your annotation to:
#RequestMapping(method = RequestMethod.POST, produces=MediaType.APPLICATION_JSON_VALUE)
And make sure you have Jackson (which Spring uses for JSON serialization) properly on your path. Also, make sure your Upload class is serializable, e.g. is not private or anything like that. If it is just a normal Java bean type class it should be fine.
Lastly, if that doesn't work you can turn on Spring debug logs with something like:
log4j.category.org.springframework.web=ALL
in your log4j.properties file.

Bad Request Errors when trying to Post large JSON data

First of all, I'm a new developer, so I apologize in advance if I'm missing something obvious.
I'm developing a web application to work offline with large amounts of data in an IndexedDB. When the user goes to the webapp, the client grabs the entire database from the server and stores it for use in the indexeddb. That works fine, but when I'm trying to use a post method to send the data (again multiple records) back to WCF, I get method not allowed or bad request when trying to send an ajax body parameter, and when I do use uri parameters, it hits the server, but not all the data is sent. I thought perhaps invalid characters may be a factor so I used the encodeURIComponent method in javascript to convert invalid characters to be valid in a uri parameter. I've also tried compressing the data with a javascript compression api called LZString. I've tried using XMLHttpRequest(which I don't fully understand). This webapp has to work offline so I can't make a server call except for initially getting data when the client first opens and for syncing data back to the server, which is why I have to send large amounts of data at a time.
I'm also using an IndexedDB wrapper called Dexie.js.
Samples of my code is below. Some code is commented, but is left to show what I've tried.
This is what I have on the server..
[OperationContract]
[WebInvoke(Method = "POST",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "REST_SendCompletedServiceOrders",
BodyStyle = WebMessageBodyStyle.Wrapped)]
[FaultContract(typeof (Exception))]
bool REST_SendCompletedServiceOrders(string compressedWebData);
This is the click event on the client used to sync back..
$('#syncCompletedData').on('click', function() {
db.ServiceOrder
.toArray(function(so) {
var completedServiceOrders = [];
for (var i = 0; i < so.length; i++) {
if (so[i].IsCompleted) {
completedServiceOrders.push(so[i]);
};
}
var customerId = sessionStorage.getItem("customerId");
var companyId = sessionStorage.getItem("companyId");
var computerId = sessionStorage.getItem("computerId");
var webData = JSON.stringify({ webCustomerId: customerId, webCompanyId: companyId, webComputerId: computerId, webServiceOrder: completedServiceOrders });
alert(webData);
alert("before compression is " + webData.length);
var URIEncodedWebData = encodeURIComponent(webData);
var JSONWebData = JSON.stringify(URIEncodedWebData);
var compressedWebData = LZString.compressToUTF16(JSONWebData);
alert("after compression is " + compressedWebData.length);
debugger;
try {
$.ajax({
type: "POST",
url: "MFSRemoteDataService/REST_SendCompletedServiceOrders",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: { compressedWebData: compressedWebData },
success: function(data) { alert(JSON.stringify(data)); },
failure: function(errMsg) {
alert(errMsg);
}
});
} catch (e) {
alert(e);
}
});
});
Before compression data length is 7707.
After compression data length is 1831.
Thanks in advance for any help, feedback, criticism, etc..
In the shown snippet, you are composing the ajax data for use in a get, which usually means you prepare a uri. However, since he is both using post and ajax, the information will be sent in the post request body and as such does not need to be encoded.
The encoding is bloating the stringified json. You can stop at webdata and post that all by itself, remove the dataType parameter in the ajax options, switch to using traditional:true in the ajax options, and it should all properly model bind.
It is hard to tell what your server side view model looks like, but if the accepting parameter is named compressedWebData (names must be exact, same goes with structure), then it would probably work like this
//code as shown in OP
//replace var webData = with the following
var compressedWebData = { webCustomerId: customerId, webCompanyId: companyId, webComputerId: computerId, webServiceOrder: completedServiceOrders };
try {
$.ajax({
type: "POST",
url: "MFSRemoteDataService/REST_SendCompletedServiceOrders",
contentType: "application/json",
data: JSON.stringify(compressedWebData),
traditional:true,
success: function(data) { alert(JSON.stringify(data)); },
failure: function(errMsg) {
alert(errMsg);
}
});
} catch (e) {
alert(e);
}
I figured out my problem. I've been trying to pass a string to the contract method, and I kept getting bad request errors. Instead, I wrapped the Json string and sent it to an object instead of a string that I created on the server.
I wrapped the JSON and sent it in the body of the ajax request..
var rawWebData = {
WebCustomerID: customerId,
WebCompanyID: companyId,
WebComputerID: computerId,
WebServiceOrders: completedServiceOrders
};
var rawData = { webData: rawWebData };
var webData = JSON.stringify(rawData);
try {
$.ajax({
type: "POST",
url: "MFSRemoteDataService/REST_SendCompletedServiceOrders",
contentType: "application/json; charset=utf-8",
dataType: "json",
traditional: true,
data: webData,
success: function (data) {
alert(JSON.stringify(data));
},
failure: function (errMsg) {
alert(errMsg);
}
});
} catch (e) {
alert(e);
}
});
Then I created a class to collect the data...
[DataContract]
public class WebServiceOrder
{
[DataMember]
public Int32 WebCustomerID { get; set; }
[DataMember]
public Int32 WebCompanyID { get; set; }
[DataMember]
public Int32 WebComputerID { get; set; }
[DataMember]
public virtual List<ServiceOrder> WebServiceOrders { get; set; }
}
Then I changed the contract method to accept the object I created instead of a string. WCF deserialized the JSON string.
[OperationContract]
[WebInvoke(Method = "POST",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "REST_SendCompletedServiceOrders",
BodyStyle = WebMessageBodyStyle.WrappedRequest)]
[FaultContract(typeof (Exception))]
bool REST_SendCompletedServiceOrders(WebServiceOrder webData);

Why ModelBinding don't work with FormData but works with RequestPayload?

I have been working with Web API and found an interesting observation that I am not able to understand.
controller:
public class UserController: ApiController
{
public void Post(MyViewModel data)
{
//data is null here if pass in FormData but available if its sent through Request Payload
}
}
viewModel
public class MyViewModel{
public long SenderId { get; set; }
public string MessageText { get; set; }
public long[] Receivers { get; set; }
}
JS that is not working
var usr = {};
usr.SenderId = "10";
usr.MessageText = "test message";
usr.Receivers = new Array();
usr.Receivers.push("4");
usr.Receivers.push("5");
usr.Receivers.push("6");
$.ajax(
{
url: '/api/User',
type: 'POST',
data: JSON.stringify(usr),
success: function(response) { debugger; },
error: function(error) {debugger;}
});
JS that is working
var usr = {};
usr.SenderId = "10";
usr.MessageText = "test message";
usr.Receivers = new Array();
usr.Receivers.push("4");
usr.Receivers.push("5");
usr.Receivers.push("6");
$.post( "/api/User", usr)
.done(function( data ) {
debugger;
});
So, if I pass on $.ajax with lots of other configuration like type, contentType, accept etc, it still don't bind model correctly but in case of $.post it works.
Can anybody explain WHY?
Try looking at what gets POSTed when you try it with $.ajax (e.g. with Fiddler of F12 tools of your choice). It can very well be that jQuery passes the data as URL-encoded string rather that as JSON literal.
To fix the issue try specifying dataType together with contentType parameter. Also, I don't think you need JSON.stringify, just pass the JSON literal you're creating:
$.ajax({
data: usr,
dataType: 'json',
contentType: 'application/json',
/* The rest of your configuration. */
});
Here's the TypeScript method that we use in one of our projects (ko.toJSON returns a string representing a JSON literal passed as a method parameter):
public static callApi(url: string, type?: string, data?: any): RSVP.Promise {
return new RSVP.Promise((resolve, reject) => {
$.ajax('/api/' + url, {
type: type || 'get',
data: data != null ? ko.toJSON(data) : null,
dataType: 'json',
contentType: 'application/json; charset=utf-8',
success: () => {
resolve.apply(this, arguments);
},
error: () => {
reject.apply(this, arguments);
}
});
});
}
Hope this helps.

HTTP POST data not going from AJAX to C# MVC 5 Backend

As the title states, I'm trying to send data from a Javascript frontend to my C# MVC 5 backend, and it doesn't seem to make it. I've run through it with the debugger in Visual Studio and the data I'm trying to pass always ends up null.
Here is my front end code:
var file = $("#my-file").files[0];
file = file[0];
var formData = new FormData();
var MyData = {
Name: "Form 133",
Attachments: {
Type: "Check"
}
};
formData.append('file', file);
$.ajax({
type: "POST",
url: '/NMISProduct/Index',
contentType: false,
processData: false,
data: { formData: formData, MyData: MyData },
success: function (result) {
console.log(result);
},
error: function (xhr, status, p3, p4) {
var err = "Error " + " " + status + " " + p3 + " " + p4;
if (xhr.responseText && xhr.responseText[0] == "{")
err = JSON.parse(xhr.responseText).Message;
console.log(err);
}
});
Backend code:
[HttpPost]
public async Task<JsonResult> Index(MyData MyData)
{
Debug.WriteLine(Request.Params["MyData"]);
Debug.WriteLine(MyData.Name);
try
{
foreach (string file in Request.Files)
{
var fileContent = Request.Files[file];
if (fileContent != null && fileContent.ContentLength > 0)
{
// get a stream
var stream = fileContent.InputStream;
// and optionally write the file to disk
var fileName = fileContent.FileName;
var path = Path.Combine(Server.MapPath("~/App_Data/"), fileName);
using (var fileStream = System.IO.File.Create(path))
{
stream.CopyTo(fileStream);
}
}
}
}
catch (Exception)
{
return Json("Upload failed");
}
return Json("File uploaded successfully");
}
public class MyData
{
public string Name { get; set; }
public Attachment Attachments { get; set; }
}
public class Attachment
{
public string Type { get; set; }
}
As I stated, on the Backend MyData always ends up being null. What am I doing wrong?
jQuery docs state:
processData (default: true)
Type: Boolean
By default, data passed in to the data option as an object (technically, anything other than a string) will be processed and transformed into a query string, fitting to the default content-type "application/x-www-form-urlencoded". If you want to send a DOMDocument, or other non-processed data, set this option to false.
The problem is that you are trying to send MyData as an object while setting processData to false. Which will result in the data not being processed before being send. The data property should contain a query string of the form key1=value1&key2=value2, or an object of the form {key1: 'value1', key2: 'value2'}, the latter will be converted into a query string before being send unless you are setting processData to false.
So if you want to send your formData along with the MyData you will need to append the MyData to the formData before sending it like so.
formData.append('Name', 'Form 133');
formData.append('Type', 'Check');
And then in the $ajax call add the FormData to the data property like so.
data: formData,
contentType setting to false you just told him to not sending any data in the header so basically the tag [HttpPost] become useless.
processData setting to false you told him not to process has and object so rendering MyData useless again by setting to true if i remember correctly it mean process anything beside string has a object.
$.ajax({
url: "#Url.Action("Your METHOD", "Your Controller")",
type: "POST",
contentType: "application/json",
data: JSON.stringify({ MyData: MyData}),
success: function(response) {
response ? alert("Process") : alert("Boom");
}
});

Categories