I have tried a myriad of different function calls, but can't seem to figure out how to trigger a download for a CSV in EmberJs.
Here is my latest code:
let endpoint = '/api/foo/';
let options = {
url: endpoint,
type: 'POST',
data: {},
dataType: 'text'
};
return new Ember.RSVP.Promise((resolve, reject) => {
options.success = function(result) {
var uri = 'data:application/csv;charset=UTF-8,' + encodeURIComponent(result);
window.open(uri, 'foo.csv');
};
options.error = (xhr, errorThrown) => {
console.log('error');
// return Ember.run(null, reject, this.didError(xhr, xhr.status, xhr.responseJSON, 1));
};
Ember.$.ajax(options);
});
This code doesn't raise any server or client side errors. It's getting a 200 response. No javascript errors, and doesn't console log anything, so I know it's not hitting the error block. But... it won't trigger the file download on the client. Does anyone know what is missing?
I am unable to test this, but I believe your issue is with returning a new promise, whereas what you really want is to return the promise itself.
So change your code to:
let endpoint = '/api/foo/';
let options = {
url: endpoint,
type: 'POST',
data: {},
dataType: 'text'
};
return Ember.RSVP.Promise((resolve, reject) => { // note the deletion of new
options.success = function(result) {
var uri = 'data:application/csv;charset=UTF-8,' + encodeURIComponent(result);
window.open(uri, 'foo.csv');
};
options.error = (xhr, errorThrown) => {
console.log('error');
// return Ember.run(null, reject, this.didError(xhr, xhr.status, xhr.responseJSON, 1));
};
Ember.$.ajax(options);
});
In implementing a similar piece of functionality, I went for a different route. Rather than creating an AJAX request, I create a form and submit it to the server. My API endpoint will then return the CSV in the response, with the appropriate Content-Disposition headers, and the browser will just download the file.
(Depending on your authentication scheme, you may have to include your authentication token as a value in your form data).
Example code below. You'll see I'm adding the auth token. The form's action URL is set in the markup of the page, but you could set it dynamically here if you wanted.
csvDownload () {
let form = Ember.$('#csvdownloadform')
let input = Ember.$('#csvdownloadtoken')
input.val(this.get('session').get('session.content.authenticated.token'))
form.submit()
input.val('')
form_func.val('')
},
Related
I want to retrieve some data from my REST API in Node to the user directly downloaded in json file when he clicks a link.
I tried some stuff using Blop, but it is not working in my case I don't understand why...
HTML
<div class='wrapper_align_vertical'>
<a class='shortCut' id='extract'>Extract</a>
</div>
JS (sample that is working)
Viewed this example on internet and worked well with sample data.
document.getElementById('extract').onclick = function(event){
var data = { a: 1, b: "hello" }
var json = JSON.stringify(data)
var blob = new Blob([json], {type: "octet/stream"})
var url = window.URL.createObjectURL(blob)
this.href = url
this.target = '_blank'
// target filename
this.download = 'data.json'
}
But in my case I am calling API to gather data from database before creating the blop.
How I am getting data
I am calling the API with ajax request.
function getFromDB(){
return new Promise(function(resolve, reject){
$.ajax({
url: '/api/extract',
dataType: "json",
type: 'GET',
headers: {
"x-access-token" : getCookie('token')
},
success: function(data){
if(data.success == 'true'){
resolve(data)
} else{
reject('Promise getFromDB failed !')
}
}
})
})
}
EDIT : I added error verification in case the promise fail, and it is not the case unfortunately ! Moreover I printed the data in the console and everything seems fine, it is complete... Resolve data is sending back the right json object.
What return the route /api/extract
The route get data from DB.
router.get('/', function(req, res, next){
var query = 'SELECT ...'
connection.query(query, function(err, rows){
var result = []
if(!err){
for(label in rows){
result.push(rows[label].path)
}
res.json({
success: true,
data:{
quantity : rows.length,
image: result
}
})
}else{
res.json({
success: false,
message: 'Failure while extracting tags from database !'
})
}
})
})
What I want to do
But it is absolutely not working, maybe I am missing something ?
document.getElementById('extract').onclick = function(event){
getFromDB()
.then(function(data){
var json = JSON.stringify(data)
var blob = new Blob([json], {type: "octet/stream"})
var url = window.URL.createObjectURL(blob)
this.href = url
this.target = '_blank'
this.download = 'data.json'
}, function(err){
// Promise failed
console.log(err)
})
}
More informations
I saw something else that might help debugging... While using the API with Postman I am getting header 200 :
GET /api/extract 200 5.484 ms - 375
BUT ! When it's through the ajax call, I get code 304 :
GET /api/extract 304 6.339 ms - -
If you have any advice or suggestion I'll appreciate, thank you !
In python I send files like this:
with open('D:\\someimage.jpg', 'rb') as image:
imager = image.read()
files = {'image': imager}
r = requests.post(url, files=files)
I want to do similar thing in js, but I always get 400 Bad Request error. The problem, I assume, is that I don't know what type the file should have. I've tried passing it in initial 'file' type, as array buffer, as binary string - nothing works. Here is my code:
var reader = new FileReader();
reader.readAsArrayBuffer(aFiles[0]);
reader.onload = function () {
var arrayBuffer = this.result,
array = new Uint8Array(arrayBuffer),
binaryString = String.fromCharCode.apply(null, array);
jQuery.ajax({
url: '/streamer',
method: 'POST',
files: {'image': binaryString},
success: function(response) {
alert(response);
},
error: function(xhr, status, error) {
alert(JSON.parse(xhr.responseText));
}});
You can send the file as a FormData (multipart/form-data) if you need to send more fields or as a Blob if you just want to send the binary directly.
jQuery tries to be smart about processing the data you send. But jQuery don't understand FormData or blobs, it sees it as an object and do the wrong thing. that's why you need to set processData = false and don't set the wrong request content-type by doing contentType = false and just let the xhr do that itself.
var fd = new FormData();
fd.append('image', aFiles[0] /*, optional filename */)
var req = jQuery.ajax({
url: '/streamer',
method: 'POST',
data: fd, // sends fields with filename mimetype etc
// data: aFiles[0], // optional just sends the binary
processData: false, // don't let jquery process the data
contentType: false // let xhr set the content type
});
// jQuery is promise A++ compatible and is the todays norms of doing things
req.then(function(response) {
console.log(response)
}, function(xhr) {
console.error('failed to fetch xhr', xhr)
})
But you don't really need jQuery for this things if you only support the latest browser that has the fetch api
var req = fetch('/streamer', {
method: 'post',
body: fd /* or aFile[0]*/
}); // returns a promise
req.then(function(response) {
// returns status + response headers
// but not yet the body,
// for that call `response[text() || json() || arrayBuffer()]` <-- also promise
if (res.ok) {
// status code was 200-299
} else {
// status was something else
}
}, function(error) {
console.error('failed due to network error or cross domain')
})
I did write a Javascript code to upload a file via API:
function submitForm(bucket, accessToken) {
console.log("Fetching the file...");
var input = document.getElementsByTagName('input')[0];
var name = input.files[0].name;
var uploadUrl = 'https://www.googleapis.com/upload/storage/v1/b/'+ bucket +'/o?uploadType=media&access_token=' + accessToken +'&name=' + name;
fetch(uploadUrl, {
method: 'POST',
body: input.files[0]
}).then(function(res) {
console.log('Something did happen!'); // <<----- Message never displayed!
});
}
However, I am not able to get the response body from the post request. The upload went well, but without a callback I cannot control the result in a deterministic way. How can I fix it, at least getting the HTTP Status Code?
Solved in a very stupid way, by adding:
event.preventDefault();
Source: What does status=canceled for a resource mean in Chrome Developer Tools?
fetch can generate an error and, if it does, that happens through the .catch.
Like this:
function submitForm(bucket, accessToken) {
console.log("Fetching the file...");
var name = 'testName';
var uploadUrl = 'https://www.googleapis.com/upload/storage/v1/b/'+ bucket +'/o?uploadType=media&access_tokenn=' + accessToken +'&name=' + name;
fetch(uploadUrl, {
method: 'POST',
body: 'body stuff'
}).then(function(res) {
console.log('Something did happen!'); // <<----- Message never displayed!
console.log(res);
}).catch(function(err) {
console.error('Got error:', err);
});
}
submitForm('12345', 'testing');
I am attemting to add an item to a sharepoint list from an Apache Cordova application. It first prompts the user to login then it will make a HTTP Post to so the data entry.
I have the following code:
function saveToSharepoint(data) {
var authority = "https://login.microsoftonline.com/common/";
var authContext = new Microsoft.ADAL.AuthenticationContext(authority);
var authResult = authContext.acquireTokenAsync("https://my.sharepoint.com", "4be098f8-2184-4831-9ef7-3d17dbbef6a0", "http://localhost:4400/services/office365/redirectTarget.html")
.then(FormatAndUpload(authResult, data), errorCallback);
}
function FormatAndUpload(authResponse, data) {
var token = authResponse.accessToken;
var expiry = authResponse.expiresOn;
console.log("Token acquired: " + authResponse.accessToken);
console.log("Token will expire on: " + authResponse.expiresOn);
$.ajax({
url: "https://my.sharepoint.com/_api/web/lists/getbytitle('" + Test + "')/items",
type: "POST",
contentType: "application/json;odata=verbose",
data: JSON.stringify(data),
headers: {
"Accept": "application/json;odata=verbose",
"Authoriztion":"Bearer " + token
},
success: function (data) {
success(data);
},
error: function (data) {
failure(data);
}
});
}
The problem I am having is that the FormatAndUpload method is being called before acquireTokenAsync has completed, so the authResponse variable passed into the FormatAndUpload method is null.
I'm not too familiar with the promise framework in Javascript/JQuery but I was under the impression that the event should only fire on completion of the previous method.
Does anyone have any pointers in how I can correctly wait for the login to complete before attempting the POST?
what you did FormatAndUpload(authResult, data); is wrong the correct way to pass a callback is
.then(function(authResult){
FormatAndUpload(authResult, data);
}, errorCallback);
so your saveToSharepoint will be like this
function saveToSharepoint(data) {
var authority = "https://login.microsoftonline.com/common/";
var authContext = new Microsoft.ADAL.AuthenticationContext(authority);
var authResult = authContext.acquireTokenAsync("https://my.sharepoint.com", "4be098f8-2184-4831-9ef7-3d17dbbef6a0", "http://localhost:4400/services/office365/redirectTarget.html")
.then(function(authResult){
FormatAndUpload(authResult, data);
}, errorCallback);
}
Thanks for the answer Newbee Dev, you were correct in that I didn't formulate the then method correctly.
For any others who see this regarding SharePoint, I actually reformatted the code for it to work as expected, so the saveToSharepoint method looks like so:
function saveToSharepoint(data) {
var AuthenticationContext = Microsoft.ADAL.AuthenticationContext;
AuthenticationContext.createAsync("https://login.microsoftonline.com/common/")
.then(function (authContext) {
authContext.acquireTokenAsync(
"https://my.sharepoint.com", // Resource URI
"4be098f8-2184-4831-9ef7-3d17dbbef6a0", // Client ID
"http://localhost:4400/services/office365/redirectTarget.html" // Redirect URI
).then(function (authResult) {
FormatAndUpload(authResult, data);
}, function (err) {
console.log(err);
});
}, function (err) {
console.log(err);
});
}
The main thing to note is creating the AuthenticationContext asynchronously and this way, the FormatAndUpload calls after the whole login process is complete. Just thought I would post this for other people who see this regarding Sharepoint and are stuck.
I have been using ajax to get back some json data and recently tried using the fetch implementation.
I am having different responses, my ajax returns a string with all my key/value pairs, while the fetch query is returning response objects which do not at all contain any of my key/value pairs. (I am requesting the exact same resource in both examples and receiving different responses)
Could anyone let me know what Im doing wrong or why this is happening?
ajax request:
$.ajax({
url: "/" + name + ".json",
dataType: "text",
success: function (data) {
console.log(data);
var json = $.parseJSON(data);
var itemArray = [];
$.each(json, function() {
itemArray.push( { value: this.id, label: this.name } );
});
//Populate the List
populateListBox(name, itemArray);
}
});
console log result: (this is the response I want to be getting using the fetch method)
[{"id":1,"name":"two on two","abbreviation":"2v2","inhouse":true,"length":50,"capacity":1,"price":"50.2","salary":"15.22","url":"http://localhost:3000/en/products/1.json"},{"id":2,"name":"threesome Lessons","abbreviation":"3SUM","inhouse":false,"length":50,"capacity":3,"price":"33.33","salary":"11.11","url":"http://localhost:3000/en/products/2.json"},{"id":3,"name":"Prod1","abbreviation":"PRR1","inhouse":true,"length":22,"capacity":2,"price":"20.0","salary":"20.0","url":"http://localhost:3000/en/products/3.json"}]
fetch request:
fetch("/" + name + ".json")
.then(function(response) {
console.log(response);
return response.json()
}).then(function(json) {
var itemArray = populateItemArray(this, json);
populateListBox(name, itemArray);
}).catch(function(ex) {
console.log('parsing failed', ex)
});
console log result: (Response an object full of other objects but seems to be only an html response without any of my requested data)
Response {}
body: (...)
bodyUsed: false
headers: Headers
ok: true
status: 200
statusText: "OK"
type: "basic"
url: "http://localhost:3000/login?locale=en"
proto: Response
I am also receiving an error in the console using the fetch method which states the following: **
SyntaxError: Unexpected token <
**
Hope someone can assist. :)
UPDATE: After closely inspecting my headers in both requests I noticed the following: My AJAX request sends through a CSRF token, as well as a cookie in the header.
All fetch requests are made as anonymous and unauthenticated (by default)
All that was needed was to add an option to the fetch request as follows:
fetch("/" + name + ".json", **{ credentials: 'same-origin' }**)
.then(function(response) {
return response.json()
}).then(function(json) {
var itemArray = populateItemArray(json);
itemArray = sortByLabel(itemArray, 'label');
populateListBox(name, itemArray);
}).catch(function(ex) {
console.log('parsing failed', ex)
});
Problem is solved! Took me long enough - the CSRF token is not needed but the cookie is definitely required as that is what allows the request to be an authenticated one. :)
fetch requires a parameter to make its requests authenticated:
credentials: 'same-origin'
Not sure, maybe change dataType: "text" to "json"?