This and this answer is my motivation for trying to convert an object to file type and append to FormData as so:
var fileBag = new FormData();
fileArray.forEach(function(element) {
file_name = element.name;
file_type = element.type;
var file = new File([element], file_name, {type:file_type});
console.log("file type= ", typeof file); // o/p is object
fileBag.append(file_name, file);
});
When I tried the same, typeof newly created file is object (I expect it to be file) after being converted to file.
File object is same before and after conversion as so:
File
lastModified: 1543472205025
lastModifiedDate: Thu Nov 29 2018 11:46:45 GMT+0530 (India Standard Time) {}
name: "testFile.xlsx"
size: 1119910
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
webkitRelativePath: ""
__proto__: File
This is how I am sending via Ajax
$.ajax({
url: '/project/url/',
type: 'POST',
data: {'file': fileBag},
enctype: 'multipart/form-data',
processData: false,
contentType: false,
dataType: "json",
success: function (data) {
console.log("data= ",data);
},
error: (error) => {
console.log(JSON.stringify(error));
}
});
I have tried this in both Django and PHP. I am getting empty arrays. My question is:
1) Will the typeof before and after conversion will be the same?
2) How can I send file object arrays to backend? Is there any other solution?
Here is my codepen of the same.
Edit: Codepen code
var fileArray = [];
var fileBag = new FormData();
function handleFileUpload(event)
{
var fileUploadButtonId = event.currentTarget.id;
var files = document.getElementById(fileUploadButtonId).files;
var fileBag = new FormData();
var arrayOfAllUploadedFiles = Array.from(files);
for(var i=0; i<arrayOfAllUploadedFiles.length; i++)
{
fileArray.push(arrayOfAllUploadedFiles[i]);
}
fileArray.forEach(function(element) {
file_name = element.name;
file_type = element.type;
var file = new File([element], file_name, {type:file_type});
console.log("file type= ", typeof file); // o/p is object. // o/p is object. Shouldn't this be 'file'?
fileBag.append(file_name, file);
alert("filebag= ",fileBag); // this is empty
});
}
Will the typeof before and after conversion will be the same?
Because "file" isn't a JavaScript data type. Everything that isn't a primitive is an object.
console.log(typeof 1);
console.log(typeof "1");
console.log(typeof null);
console.log(typeof undefined);
console.log(typeof [1,2,3]);
console.log(typeof {hello: "world"});
console.log(typeof new Date());
console.log(typeof document.body);
console.log(typeof window);
2) How can I send file object arrays to backend? Is there any other solution?
Understand how data is formatted when you send it over HTTP
Understand the limitations of jQuery
You can't send an array. You can only send some text which server-side code can assemble into an array.
For both standard encodings supported by forms, PHP will populate $_POST with the data in them.
If the name of a field in that data ends in [], PHP will put an array in $_POST.
If you pass an array to jQuery ajax:
data: { someValue: [1, 2, 3] }
Then jQuery will encode it as:
someValue[]=1&someValue[]=2&someValue[]=3
… so PHP will generate an array.
However, the values have to be things that jQuery understands. It can't handle FormData objects.
If you want to send a FormData object (which you have to do in order to send files) then you need to send it instead of an object:
jQuery.ajax({
url: "/foo",
data: a_form_data_object,
processData: false,
contentType: false,
});
You need processData so it doesn't try to convert the FormData object (i.e. so it will let XMLHttpRequest do that). You need contentType to stop it overriding the one XMLHttpRequest will generate from the FormData object.
So then to send an array, we go back to the rules I described above. You said:
fileBag.append(file_name, file);
But to get an array you need a name ending in [].
fileBag.append("myname[]", file);
Finally, not visible in your question, but looking at your codepen. This will give you a set of files:
document.getElementById(fileUploadButtonId).files;
Since they are files, using code to convert them to files makes no sense.
You can simplify your code:
var fileBag = new FormData();
var files = document.getElementById(fileUploadButtonId).files;
var arrayOfAllUploadedFiles = Array.from(files);
fileArray.forEach(file => fileBag.append("myname[]", file);
jQuery.ajax({
url: "/foo",
data: fileBag,
processData: false,
contentType: false,
});
Related
I'm executing an ajax call to a external api (this cannot be modified) to upload an store a file into a folder. This request must return a path (ex. "C:\Doctos\File.pdf" but after a console.log is returning something like this:
#document < string xmlns="http://tempuri.org/">"C:\Doctos\File.pdf"
So my question is, what can I do to get only the text that I want without any change in the api (because I'm not able to do it).
Here is the ajax call that I'm using.
PD. This ajax call is using the provided structure for the dev team that developed the api so things like dataType also cannot be modified
var data = new FormData();
var files = $('#fileUpload').get(0).files;
if (files.length > 0) {
data.append("UploadedFile", files[0]);
}
$.ajax({
type: 'POST',
url: 'api/v1/moreurl/UploadFile',
contentType: false,
processData: false,
data: data,
success: function (data) {
var res = data;
//Returns above example
console.log(res);
//Returns something like <p>[object XMLDocument]</p>
$('#MyInput').attr('src', res);
}
});
I would use regular expressions to get the desired string from received data. Put this after success line.
var regex = />\"(.*)\"/;
var matched = regex.exec(data);
var result = matched[1];
console.log(result);
The regex matches the last quoted string in your example.
You can get the data in the xml with jQuery
$.ajax({
type: 'POST',
url: 'api/v1/moreurl/UploadFile',
contentType: false,
processData: false,
data: data,
success: function (data) {
// Get the contents of the xml
var file = $(data).find('string').text();
$('#MyInput').attr('src', file);
}
});
Just like the title says, how do I send a formdata object to a mvc controller with both a json object (including nested objects) and list of files.
I have already tried to stringify the object to a json object but the controller can not read the property, it reads the file list without problems.
Here is the controller method:
[HttpPost]
public IActionResult CreateTask(Task task, IEnumerable<IFormFile> files)
{
//some code
}
here is my javascript:
function createTask() {
var formData = new FormData();
var files = //some file objects
var obj = {
//some parameters
};
var task = JSON.stringify(task);
formData.append("task", task);
formData.append("files", files);
console.log(task);
$.ajax({
type: "POST",
url: "/Task/CreateTask",
processData: false,
contentType: false,
data: formData,
success: function (data) {
},
error: function (data) {
}
})
}
I need the controller method to read both the task and the file list at the same time if this is possible.
The only way to do this would be to bind the JSON sent as task to a string server-side. Then, you'd have to manually deserialize it into an object. In case it's not obvious, that also means you won't get any validation on any of the members of that JSON object. It will just be a string as far as ASP.NET Core and the modelbinder is concerned.
That said, I think the issue here is that you're needing to upload files and think that that necessitates posting as multipart/form-data. You can actually post as JSON and yet still include file uploads. That requires two changes, though:
You must bind the file "uploads" to byte[]s, instead of IFormFiles, server-side.
Client-side, you must add them to the JSON object you're posting as either Base64-encoded strings or uint8 arrays.
The first part is relatively straight-forward. The JSON deserializer invoked by the modelbinder will automatically convert Base64-encoded strings to byte array, and of course a JS unint8 array is essentially just a byte array, anyways.
The second part probably bears a bit more discussion. You'll need to need to use the File API to read the upload file data, and then convert that into either a Base64-encoded string or uint8 array:
Base64
var reader = new FileReader();
reader.onload = function(e) {
let base64 = btoa(reader.result);
myJsonObject.files.push(base64);
}
reader.readAsBinaryString(file);
Byte Array
var reader = new FileReader();
reader.onload = function(e) {
let bytes = Array.from(new Uint8Array(reader.result));
myJsonObject.files.push(bytes);
}
reader.readAsArrayBuffer(file);
You could try to convert object to form-data like below:
View
<script type="text/javascript">
$(document).ready(function () {
$("input").change(function () {
var formData = new FormData();
var files = $("#files")[0].files;
var obj = {
id: 1,
name: "jack"
};
for (var key in obj) {
formData.append(key, obj[key]);
}
for (var key in files) {
formData.append("files", files[key]);
}
$.ajax({
type: "POST",
url: "/api/values/CreateTask",
processData: false,
contentType: false,
data: formData,
success: function (data) {
},
error: function (data) {
}
})
});
});
</script>
Controller
[HttpPost]
public IActionResult CreateTask([FromForm]Task task, [FromForm]IEnumerable<IFormFile> files)
{
return Ok("Success");
}
I have written a script in JavaScript to handle a file drag and drop. When the 'drop' listen is called the file is captured using dataTransfer.files (function below).
event.preventDefault();
event.stopPropagation();
console.log(event.dataTransfer.files[0]);
uploadFiles = event.dataTransfer.files;
fileBoxUpLoad(uploadFiles);
Console log shows the file appears to be capture correctly
File {name: "Changi - 2016.pdf", lastModified: 1473382409845, lastModifiedDate: Fri Sep 09 2016 10:53:29 GMT+1000 (Australian Eastern Standard Time), webkitRelativePath: "", size: 197754, …}
The fileBoxUpLoad function is call and when the code gets to the xmlhttp.send it throws an error
Unexpected token o in JSON at position 1
at JSON.parse ()
var formData = new FormData();
for(var x=0; x<=item.length; x++)
{
formData.append('file', item[x]);
}
var xmlhttps = new XMLHttpRequest();
xmlhttps.open("POST", uri);
xmlhttps.setRequestHeader('Content-Type', file.type);
xmlhttps.send(formData);
I understand this means I am trying to parse an Javascript object, when I don't think I am and I can't see where my code is any different to all the tutorials I have read. Any advice? Thanks!!
You can do something like this
//declare an array for store the files
var uploadedFiles = [];
//then get files by using their id or maybe you can loop through on your control
var file = $('input[name=YourControlName]').get(0).files[0];
//push the files to array
uploadedFiles.push(file);
//declare form data and append files to form data
var formData = new FormData();
for (var i = 0; i < uploadedFiles.length; i++) {
formData.append("file" + i, uploadedFiles[i]);
}
//then you can post to via ajax and get on api via request files
$.ajax({
type: "post",
url: "",
contentType: false,
data: formData,
processData: false,
success: function (result) {
alert(result);
},
failure: function (e) {
alert(e);
}
});
I'm trying to convert an array of Hazards(class that i created) to JSON,
this is my code:
$.ajax({
async: true,
url: web + "/GetHazards",
method: "POST",
contentType: "application/json",
success: function (data) {
var res = data.d;
var i;
alert(res[0]);
the returned data is like this :
"[{\"Hazard_ID\":3014,\"Hazard_Lat\":32.2615929,\"Hazard_Long\":35.01423},{\"Hazard_ID\":3013,\"Hazard_Lat\":32.3426857,\"Hazard_Long\":34.9103165},{\"Hazard_ID\":3012,\"Hazard_Lat\":32.3426857
My server side code returns the correct values that i need, but the problem is when i alert the res[i] it behave like res is a string and alerts me "["
what i need to get is
{\"Hazard_ID":3014,\"Hazard_Lat\":32.2615929,\"Hazard_Long\":35.01423}
i dont know if it mind this is my server-side code by the way:
{
List<Returned_Hazard> rh = new List<Returned_Hazard>();
JavaScriptSerializer json = new JavaScriptSerializer();
.
.
.
while (reader.Read())
{
Returned_Hazard RH = new Returned_Hazard(
int.Parse(reader[0].ToString()),
float.Parse(reader[1].ToString()),
float.Parse(reader[2].ToString())
);
rh.Add(RH);
}
command.Connection.Close();
return json.Serialize(rh);
}
You need to parse the JSON, using JSON.parse:
var data = { d: "[{\"Hazard_ID\":3014,\"Hazard_Lat\":32.2615929,\"Hazard_Long\":35.01423},{\"Hazard_ID\":3013,\"Hazard_Lat\":32.3426857,\"Hazard_Long\":34.9103165}]"
};
var res = JSON.parse(data.d);
console.log(res[0].Hazard_ID); //3014
I am currently trying to solve a problem.
I have several forms on a single page which get sent to the backend asynchronously via ajax.
Now some of them need to have a fileupload which doesnt break the process, so it alsoneeds to be handled asynchronously.
I am trying to figure it out like that :
// Allgemein Submit
$allgSubmit.click(function(){
event.preventDefault();
var gehrKundennummer = $('#gehrKundennummer').val();
var kundenklasse = $("input[type='radio'][name='kundenklasse']:checked").val();
var lkw12t = $('#lkw12t').val();
var lkw3t = $('#lkw3t').val();
var autobus = $('#autobus').val();
var firmenname1 = $('#firmenname1').val();
var firmenname2 = $('#firmenname2').val();
var uidnummer = $('#uidnummer').val();
var peselregon = $('#peselregon').val();
var firmenart = $('#firmenart option:selected').val();
var strasse = $('#strasse').val();
var ort = $('#ort').val();
var plz = $('#plz').val();
var land = $('#land').val();
var fd = new FormData();
var file = fd.append('file', $('#allg_firmen_dok').get(0).files[0]);
var allgArray = {
'gehrKundennummer':gehrKundennummer,
'kundenklasse':kundenklasse,
'lkw12t':lkw12t,
'lkw3t':lkw3t,
'autobus':autobus,
'firmenname1':firmenname1,
'firmenname2':firmenname2,
'uidnummer':uidnummer,
'peselregon':peselregon,
'firmenart':firmenart,
'strasse':strasse,
'ort':ort,
'plz':plz,
'land':land,
'file':file
};
//var data = new FormData();
//jQuery.each(jQuery('#allg_firmen_dok')[0].files, function(i, file) {
// data.append('file-'+i, file);
//});
console.log(allgArray);
$.ajax({
url: "PATHTOFILE/logic/logic_update_client_allg.php",
type: "POST",
data: allgArray,
processData: false, // tell jQuery not to process the data
contentType: false,
success: function(allgArray){
alert(allgArray);
var allgSave = $('#allgSave');
allgSave.text('Aktualisieren erfolgreich!');
allgSave.toggle();
},
error: function(){
var allgSave = $('#allgSave');
allgSave.text('Aktualisieren fehlgeschlagen!');
allgSave.toggle();
}
});
});
The console log of the array returns all values correctly except the one for "file"
it says undefined.
I don't know how to deal with it, are there any requirements that im missing?
Thanks for any kind of help
EDIT
var file = fd.append('file', $('#allg_firmen_dok').get(0).files[0]);
returns undefined
I think the variable fd = new FormData() is an Object and it has attribute "file". So it cannot pass the attribute "file" to another Object "allgArray"
You need to check about it before you call function
$.ajax({
url: "PATHTOFILE/logic/logic_update_client_allg.php",
type: "POST",
data: allgArray,
Think about the data you send! It maybe another instance to get data from "file" of "fd". Hope it help you! ^^
Btw, I used AJAX to send file last time
$(document).ready(function (e) {
$("#Form").on('submit',(function(e) {
e.preventDefault();
$.ajax({
url: "uploader.php", // Url to which the request is send
type: "POST", // Type of request to be send, called as method
data: new FormData(this), // Data sent to server, a set of key/value pairs (i.e. form fields and values)
contentType: false, // The content type used when sending data to the server.
cache: false, // To unable request pages to be cached
processData:false, // To send DOMDocument or non processed data file it is set to false
success: function(data) // A function to be called if request succeeds
{
console.log(data);
}
});
}));
});
add headers: { "Content-Type": "multipart/form-data" } in ajax option