I am sending a XMLHttpRequest to a MVC Controller and i am expecting to receive a file as a result.
When debugging with the browser i am getting a response that is ok , but i do not know why it is not as a file:
JS
window.submit=function () {
return new Promise((resolve, reject) => {
var form = document.getElementById("newTestForm");
var data = new FormData(form);
var xhr = new XMLHttpRequest();
var method = form.getAttribute('method');
var action = form.getAttribute('action');
xhr.open(method, action);
xhr.onload = function () {
if (this.status >= 200 && this.status < 300) {
resolve(xhr.response); //response looks ok...but no file starts downloading
}
else if (xhr.status != 200) {
reject("Failed to submit form with status" + xhr.status);
}
}
xhr.send(data);
});
}
Controller
[HttpPost]
[Route([Some Route])]
public async Task BenchAsync(object request)
{
try
{
string fileName = "results.txt";
object result = await service.RunAsync(request);
byte[] data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(result));
this.Response.ContentType = "application/octet-stream";
this.Response.ContentLength = data.Length;
using(MemoryStream stream=new MemoryStream(data))
{
await stream.CopyToAsync(this.Response.Body);
}
}
catch (Exception ex)
{
throw;
}
}
I have solved it thanks to this Post
It seems i had to transform the response into a BLOB , create a download link and point it towards this blob and the created link in order to download the file.
So the function looks like :
window.submit= function () {
return new Promise((resolve, reject) => {
var form = document.getElementById("newTestForm");
var data = new FormData(form);
var xhr = new XMLHttpRequest();
var method = form.getAttribute('method');
var action = form.getAttribute('action');
xhr.open(method, action);
xhr.onload = function () {
if (this.status >= 200 && this.status < 300) {
var blob = new Blob([this.response], { type: 'image/pdf' });
let a = document.createElement("a");
a.style = "display: none";
document.body.appendChild(a);
let url = window.URL.createObjectURL(blob);
a.href = url;
a.download = 'mytext.txt';
a.click();
window.URL.revokeObjectURL(url);
}
else if (xhr.status != 200) {
reject("Failed to submit form with status" + xhr.status);
}
}
xhr.send(data);
});
}
P.S i do not know what the type of the blob is named for txt but it works as well , given the right extension.
My function has to call two endpoints and concat them in one string at the same time. My code is simply a function that is getting two endpoints at the same time and print it in console.
But the same function has to concat them to one string.
I tried to create separated variables contains each call and then simply concat them, but the result hadn't been any different.
I read about it for couple of hours, and I see no, even the smallest tip anywhere.
EDIT: Please mind that each endpoint is an actual array.
function endpointsToOneString() {
const Http = new XMLHttpRequest();
const url = 'https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json';
Http.open("GET", url);
Http.send();
Http.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
console.log(Http.responseText)
}
}
const HttpTwo = new XMLHttpRequest();
const urlTwo = 'https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json';
HttpTwo.open("GET", urlTwo);
HttpTwo.send();
HttpTwo.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
console.log(Http.responseText)
}
}
}
endpointsToOneString();
In this case you should use Promise feature of javascript.
Here you can learn how to promisify your native XHR. Morever, Here you can find about promise chaining.
I have just added Promise in your code but it needs to be refactored.
Update: From comment, you want your response texts as a plain string. But we are actually getting a JSON array as response. So, we need to parse it using JSON.parse() function to make it an array object. Then we need to use .join() method to join all element of the array into a string. See the code below:
function endpointsToOneString() {
var requestOne = new Promise(function(resolve, reject){
const Http = new XMLHttpRequest();
const url = 'https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json';
Http.open("GET", url);
Http.onload = function () {
if (this.status >= 200 && this.status < 300) {
resolve(Http.response);
} else {
reject({
status: this.status,
statusText: Http.statusText
});
}
};
Http.onerror = function () {
reject({
status: this.status,
statusText: Http.statusText
});
};
Http.send();
});
var requestTwo = new Promise(function(resolve, reject){
const HttpTwo = new XMLHttpRequest();
const urlTwo = 'https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json';
HttpTwo.open("GET", urlTwo);
HttpTwo.onload = function () {
if (this.status >= 200 && this.status < 300) {
resolve(HttpTwo.response);
} else {
reject({
status: this.status,
statusText: HttpTwo.statusText
});
}
};
HttpTwo.onerror = function () {
reject({
status: this.status,
statusText: HttpTwo.statusText
});
};
HttpTwo.send();
});
Promise.all([
requestOne,
requestTwo
]).then(function(result){
var response = JSON.parse(result[0]).join();
response += JSON.parse(result[1]).join();
console.log(response);
});
}
endpointsToOneString();
I understand you want to concat the result of two parallel requests. In that case you can use a library like axios. From their docs
function getUserAccount() {
return axios.get('/user/12345');
}
function getUserPermissions() {
return axios.get('/user/12345/permissions');
}
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {
// Both requests are now complete
}));
So for your example:
function getEndpoint1() {
return axios.get('https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json');
}
function getEndpoint2() {
return axios.get('https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json');
}
axios.all([getEndpoint1(), getEndpont2()])
.then(axios.spread(function (resp1, resp2) {
// Both requests are now complete
console.log(resp1 + resp2)
}));
try to have a look on the Promise.all method:
https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
As in this answer you should wrap your XHR in a Promise and then handle resolving of all function call. In this way you can access endpoint results in order.
Here's a working fiddle:
function makeRequest(method, url) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onload = function() {
if (this.status >= 200 && this.status < 300) {
resolve(xhr.response);
} else {
reject({
status: this.status,
statusText: xhr.statusText
});
}
};
xhr.onerror = function() {
reject({
status: this.status,
statusText: xhr.statusText
});
};
xhr.send();
});
}
let url1 = 'https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json';
let url2 = 'https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json'
Promise.all([makeRequest('GET', url1), makeRequest('GET', url2)])
.then(values => {
debugger;
console.log(values);
});
https://jsfiddle.net/lbrutti/octys8k2/6/
Is it obligatory for you to use XMLHttpRequest? If not, u had better use fetch, because it returns Promise and with Promise it would be much simpler.
Rather than immediately printing them, save them to local variables, then print them at the end:
function endpointsToOneString() {
let response; // this line here declares the local variable
results = 0; // counts results, successful or not
const Http = new XMLHttpRequest();
const url = 'https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json';
Http.open("GET", url);
Http.send();
Http.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
response = Http.responseText; //save one string
}
if (this.readyState == 4) {
results++;
}
}
const HttpTwo = new XMLHttpRequest();
const urlTwo = 'https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json';
HttpTwo.open("GET", urlTwo);
HttpTwo.send();
HttpTwo.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
response += HttpTwo.responseText // save the other string
}
if (this.readyState == 4) {
results++;
}
}
while(results < 2) {} //loops until both requests finish, successful or not
console.log(response); //print total string
}
endpointsToOneString();
Also, HttpTwo's onreadystatechange function is calling for Http.responseText, rather than HttpTwo.responseText. Fix that as well for best results.
EDIT: Thanks for the tip, Jhon Pedroza!
EDIT: Noah B has pointed out that the above is dirty and inefficient. They are entirely correct. Better version based on their suggestion, credit to them:
function endpointsToOneString() {
let response1 = '', response2 = ''; // this line declares the local variables
const Http = new XMLHttpRequest();
const url = 'https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json';
Http.open("GET", url);
Http.send();
Http.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
response1 = Http.responseText; //save one string
checkResults(response1, response2);
}
}
const HttpTwo = new XMLHttpRequest();
const urlTwo = 'https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json';
HttpTwo.open("GET", urlTwo);
HttpTwo.send();
HttpTwo.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
response2 = HttpTwo.responseText; // save the other string
checkResults(response1, response2);
}
}
}
function checkResults(r1, r2) {
if (r1 != '' && r2 != '') {
console.log(r1 + r2);
}
}
endpointsToOneString();
function endpointsToOneString() {
var response;
const Http = new XMLHttpRequest();
const url = 'https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json';
Http.open("GET", url);
Http.send();
Http.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
response = this.responseText;
HttpTwo.open("GET", urlTwo);
HttpTwo.send();
}
}
const HttpTwo = new XMLHttpRequest();
const urlTwo = 'https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json';
HttpTwo.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
response += this.responseText;
console.log(response);
}
}
}
endpointsToOneString();
check this out. there's just minimal editing to your code.
Please I need help !
To begin I don't speaking very well english sorry for the mistakes
So I'm tryng to receive a JSON Object with this code :
function uhttp(url){
var xhr = new XMLHttpRequest();
xhr.open('get', url, true);
xhr.responseType = 'json';
xhr.onload = function() {
var status = xhr.status;
if (status == 200) {
console.log(xhr.response)
return xhr.response;
}
};
xhr.send();
console.log('exit')
};
but when I use the function https like this :
`
( ()=>{
var perso =uhttp('bd.php?table=charac')
for (var i = 0; i < perso.lenght; i++) {
document.getElementbyID('container').append('<ul>'+perso[i].nom+'</ul>')
}
})()
`
perso he's undifined...
here the console of index.html
I've the impression that we'are exit the function before that whe receive the resonse and that is why the function return a null
Of course before to ask my question I'have make some research but no one working in my case....
Thank you for yours anwsers
It happens because you aren't returning value from uhttp() function but from anonymous function (xhr.onload).
In order to access this value after the AJAX call is over use promises:
function uhttp(url){
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open('get', url, true);
xhr.responseType = 'json';
xhr.onload = function() {
var status = xhr.status;
if (status == 200) {
resolve(xhr.response);
return;
}
reject();
};
xhr.onerror = function() {
reject()
};
xhr.send();
})
}
And use it like so:
uhttp('bd.php?table=charac').then(function(result) {
var person = result;
for (var i = 0; i < perso.lenght; i++) {
document.getElementbyID('container').append('<ul>'+perso[i].nom+'</ul>');
}
}).catch(function() {
// Logic on error
});
I am using AJAX GET to get a local JSON file and it does that, but once i try to return it says undefined.
ScoreHandler = function () {
this.getScores = function() {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
var data = JSON.parse(this.responseText);
//This logs object
console.log(data);
return data;
}
};
xmlhttp.open("GET", "JSON/Scores.json", true);
xmlhttp.send();
};
};
HighScores = function (scoreHandler) {
var scoreHandler = scoreHandler;
var scores = this.scoreHandler.getScores();
//This logs undefined
console.log(scores);
}
Just implement a callback for response, something like this
ScoreHandler = function () {
this.getScores = function(callback) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
var data = JSON.parse(this.responseText);
//This logs object
console.log(data);
if(typeof callback === 'function')
callback(data);
//return data;
}
};
xmlhttp.open("GET", "JSON/Scores.json", true);
xmlhttp.send();
};
};
HighScores = function (scoreHandler) {
var scoreHandler = scoreHandler; //why this line use it directly
var scores = this.scoreHandler.getScores(function(data){
console.log("response", data); //you can see the data here
});
//This logs undefined
console.log(scores);
}
I' using JSzip to create zipfile which contain all the images files. I got the images from external links within a loop using XMLHttpRequest. According to my code zipfile create before complete the XMLHttpRequest. So it returns empty zip file. How to create zip file after looping all the files?
$(document).on('click', '.download', function(){
var path = $(this).attr("data-id");
var count = $(this).attr("value");
var storageRef = firebase.storage().ref();
var zip = new JSZip();
console.log(count);
for (i = 1; i <= count; i++) {
console.log(path+i+".png");
var imagePath = path+i+".png";
// Create a reference to the file we want to download
var starsRef = storageRef.child(imagePath);
starsRef.getDownloadURL().then(function(url) {
// Insert url into an <img> tag to "download"
ImageUrl = url;
var xhr = new XMLHttpRequest();
xhr.open('GET', ImageUrl, true);
xhr.responseType = "arraybuffer";
xhr.onreadystatechange = function(evt) {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
zip.file(i+".png", xhr.response);
}
}
};
xhr.send();
})
}
zip.generateAsync({type:"blob"})
.then(function(content) {
// see FileSaver.js
saveAs(content, "my.zip");
});
});
JSZip supports promises as content: you can wrap each HTTP calls into promises and not explicitly wait.
The first function, downloadUrlAsPromise, wraps the xhr call into a Promise. The second function, downloadFirebaseImage, chains the promise from getDownloadURL with the promise of the first function. The result is a promise of the xhr content.
Once you have that, you can give directly the promise to JSZip like that:
zip.file(i+".png", downloadFirebaseImage(imagePath));
Full methods:
function downloadUrlAsPromise (url) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = "arraybuffer";
xhr.onreadystatechange = function(evt) {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(xhr.response);
} else {
reject(new Error("Ajax error for " + url + ": " + xhr.status));
}
}
});
xhr.send();
});
}
function downloadFirebaseImage(storageRef, path) {
var starsRef = storageRef.child(imagePath);
return starsRef.getDownloadURL().then(function(url) {
return downloadUrlAsPromise(url);
});
}
// ...
for (i = 1; i <= count; i++) {
console.log(path+i+".png");
var imagePath = path+i+".png";
zip.file(i+".png", downloadFirebaseImage(imagePath));
}
zip.generateAsync({type:"blob"})
.then(function(content) {
// see FileSaver.js
saveAs(content, "my.zip");
});