Save File through JQuery AJAX Call - javascript

I am using JQuery to send an AJAX request to a Node server (using Hapi). The server responds with a PDF file correctly, but I am trying to save the file locally. The catch is that the PDF should only return if the POST sent the right data. It's "protected" in other words and I don't want to just expose it publicly.
Frontend code:
$.get('http://localhost:3000').done(function (data) {
console.log(data);
});
Backend code:
server.route({
method: 'GET',
path: '/',
handler: async (request, h) => {
return h.file('./static/sample.pdf', {
mode: 'attachment',
filename: 'sample.pdf'
})
}
});
I receive the data but nothing happens in the front-end UI. How can I download the PDF that is sent automatically?

You can achieve that with html5 using
Download it!
Notice that this works only for same-origin URLs, or the blob: and data: schemes. And this gets overridden by the Content-Disposition header from the server.
If you want to do this programatically you could do this
const element = document.createElement("a")
element.setAttribute(
"href",
// you could also use base64 encoding here like data:application/pdf;base64,
"data:text/plain;charset=utf-8," + encodeURIComponent('pdf binary content here')
)
element.setAttribute("download", "file.pdf")
element.style.display = "none"
document.body.appendChild(element)
element.click()
document.body.removeChild(element)
Anyway this is only a useful method if u want to create/modify the downloaded data from the client side, but if u are getting it as it is from the server side then its better just to open a new url, letting the browser handle it.

Related

Downloading a file from a url

I don't have much JS experience but thought this should be fairly simple - still, can't get it to work.
I need to download a file from an external server (AWS - no authentication, just plain url) when a web page loads (no clicks).
Would have been nice to just use the HTML5 'download' attribute - but doesn't work in this case.
So, have tried various JS code snippets I found.
Closest I got was:
function downloadFile(url, fileName){
fetch(url, { method: 'get', mode: 'no-cors', referrerPolicy: 'no-referrer' })
.then(res => res.blob())
.then(res => {
const aElement = document.createElement('a');
aElement.setAttribute('download', fileName);
const href = URL.createObjectURL(res);
aElement.href = href;
// aElement.setAttribute('href', href);
aElement.setAttribute('target', '_blank');
aElement.click();
URL.revokeObjectURL(href);
});
}
downloadFile('https://www.google-analytics.com/analytics.js', 'gooleAnalytics.js')
While this looks like it is working it actually downloads an empty file.
Ideas?
Cheers.
Trying to download a remote file on page load.
Resulted in empty file being downloaded.
Since you're using fetch in no CORS mode here, you won't be able to read the response of it, which is why the download is blank. Looking at the URL you provided, it doesn't send an Access-Control-Allow-Origin header so you won't be able to request it in the normal way. In order for it to work, you'll need to use a CORS proxy, use the iframe hack Albert Logic Einstein mentioned, or just have people right click save link as.

Sending image from flask to javascript

I am trying to send an image from the flask server to the web script. The first server connects to another API and gets an image. I don't want to save this image just forward it to the web application script.
#app.route('/api/image', methods=['GET'])
def return_image():
r = requests.get(f"{api_url}/image")
return send_file(
BytesIO(r.content),
mimetype='image/jpeg',
as_attachment=True,
attachment_filename='test.jpg')
I am trying to get this image by ajax request and display it.
function updateImage() {
$.ajax({
url: "/api/image",
type: "GET",
dataType: 'image/jpg',
success: function (res) {
$(theImg).attr("src", 'data:image/png;base64,'+ res);
M.toast({
html: 'Loading image: Success'
})
},
error: function () {
M.toast({
html: 'Loading image: Fail'
})
}
});
}
I tried to make this work but wasn't able to. I really appreciate your help.
At a glance your JS writes a data-uri to the src attribute, but res is actually the binary data with a image/png mimetype.
So you either need to base64 encode r.content on the server side, here's an example which actually creates the entire uri server side, then return that string and have your JS add that to the src attribute.
Or if you just want make your JS support the exisitng endpoint you could probably create a blob based on /api/image response, then write that to the img tag.

How do I get the direct link to an attached file from Google's Calendar API?

This is part of the data I receive from Google's Calendar API:
{
"fileUrl":"https://drive.google.com/file/d/1q-cSkADjUnD0PGOamZKI44oIL_cXDJlg/view?usp=drive_web",
"title":"2.png",
"iconLink":"",
"fileId":"1q-cSkADjUnD0PGOamZKI44oIL_cXDJlg"
}
How do I achieve the direct link to the file (using JavaScript) so I can use it as datasource? example:
https://something.com/2.png
Edit: This link (right click & copy image adress) works as datasource, I just don't know how I would dynamically create it.
You need to make a GET request to fileUrl, since permanent "direct" links will not exist.
Directly from these google docs:
Using alt=media
To download files, you make an authorized HTTP GET request to the file's resource URL and include the query parameter alt=media. For example:
GET
https://www.googleapis.com/drive/v3/files/0B9jNhSvVjoIVM3dKcGRKRmVIOVU?alt=media
URLs do not need file extensions for their server to understand what it is you are requesting. However, sometimes servers want an extra query parameter to decide what to send back.
So it also seems you may need to append ?alt=media onto the end of your endpoint url, fileUrl.
Then just use ajax, or a similar requesting method, and send a GET request to the endpoint.
Using the Drive API and JS:
var fileId = '0BwwA4oUTeiV1UVNwOHItT0xfa2M';
var dest = fs.createWriteStream('/tmp/photo.jpg');
drive.files.get({
fileId: fileId,
alt: 'media'
})
.on('end', function () {
console.log('Done');
})
.on('error', function (err) {
console.log('Error during download', err);
})
.pipe(dest);

How to Access To A Zip File on Server Using JQuery Ajax - No Need to Download

Can you please let me know how I can get a ZIP file stored on same server using the jquery Ajax? Please be informed that I do not want to download the file
I need to pass the result , if success? to an API snippet like this, (this is using a Form to pass a zip file from client to the request Here is The Working Demo
request({
url: portalUrl + '/sharing/rest/content/features/generate',
content: myContent,
form: dom.byId('uploadForm'),
handleAs: 'json',
load: lang.hitch(this, function (response) {
if (response.error) {
errorHandler(response.error);
return;
}
var layerName = response.featureCollection.layers[0].layerDefinition.name;
addShapefileToMap(response.featureCollection);
}),
error: lang.hitch(this, errorHandler)
});
but I need to pass the zip file from server witout using a form and here is what I would like to do
var data = "www.mydomain.com/GIS/App.ZIP";
request({
....,
form: data,
....
});
Update
As menitoned API offers the Formdata option as well but how I can pass second parameter of type inside the append method?
var theFile = "http://localhost/Portal/APP.ZIP";
var myFormData = new FormData();
myFormData.append(theFile, ? );
Javascript cannot access the local filesystem without user intervention for security reasons. A user must take an action to load a file. Otherwise, it would be very easy for malicious web pages to traverse your file system.
You can use javascript via AJAX to trigger a server side script which can access the server file system and return results to javascript though.

In Javascript how can I redirect an Ajax response in window location

I currently have the following working piece of code (angular but applies to any JS framework):
var url = '/endpoint/to/my/file';
$http({
method: 'GET',
url: url
})
.success(function(jdata) {
window.location = url;
})
.error(function(je){
// display errors on page
});
The above is called after a form was completed and the user has clicked on "submit" (the real situation is a bit more complex than this but it is the same idea). I do the form check asynchronously, so there's no page reload.
If the request is successful, returns a binary (a pdf file), if not succesful, the request returns a 400 BadRequest with errors formatted in JS. So what I do is, if successful, I redirect to the same url to have the PDF otherwise I get the JSON error object and do something with it.
How can I refrain from making two requests if the requests is successful?
Note1: on the backend side I would like to keep only one route that does everything, check + return PDF
Note2: the current situation is pretty neat in my opinion, since I have an asynchronous form check and if successful the file downloads directly in the browser since I have "CONTENT-DISPOSITION" -> "attachment" in the HTTP header of the successful response
Update: additional information about the architecture as requested by Emile:
In my use case I have one endpoint that checks inputs (and other external requirements). For security reasons I cannot output the PDF if all requirements are not satisfied so I have to do the check prior to delivering the file ( the file is automatically generated) anyway. So having two endpoints would just be redundant and add some unnecessary complexity.
While writing I think an alternative solution could be to pass an argument on the endpoint while doing the check, so that if successful, it stops and does not generate the PDF, and then redirect to the same endpoint without the flag which will output the PDF.
So I do the check twice but only load (and generate - which is resource intensive) the file only once and I have only one endpoint...
Here's the adapted code:
var url = '/endpoint/to/my/file';
$http({
method: 'GET',
url: url+'?check'
})
.success(function(jdata) {
window.location = url;
})
.error(function(je){
// display errors on page
});
On the backend side (I use Play framework/Scala)
def myendpoint(onlyDoCheck: Boolean = false) = Action{implicit request =>
myForm.bindFromRequest.fold(
e => BadRequest(myErrors),
v => if(onlyDoCheck) Ok(simpleOkResponse) else Ok(doComputationgeneratefileandoutputfile)
)
}
The real deal
The best you could do is split your endpoint.
One for the form and the convenience of having errors without refresh.
Then, on success, redirect to your other endpoint which only downloads the file.
If the file was on the disk and wasn't auto-generated and required to be authenticated to be downloaded, you could hide the file behind a normal endpoint, do the checks, and return the file using X-Accel-Redirect header with nginx or X-Sendfile using apache.
The hack
Disclaimer: This is more of a hack than the best solution. As mention by #Iceman, Safari, IE, Safari-iOS, Opera-mini and some such browsers don't support this particular spec.
In your server-side endpoint, if the file is available without errors, you can set the header to the content-type of the file (like 'application/pdf') so the download will starts automatically.
If there are errors, don't set the header and return a json of the errors to inform the user through javascript.
Since we don't know what's behind, here's a python (Django) example:
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename=your_filename.pdf'
response.write(repport.printReport(participantId))
return response
You can handle the response in the ajax success callback:
$.ajax({
url: 'endpoint.php',
success: function(data) {
var blob = new Blob([data]);
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = "filename.pdf";
link.click();
}
});
You could also try the jQuery fileDownload plugin mentioned in this answer.

Categories

Resources