download file and then leave page - javascript

$http.post('#Url.Action("donePressed")',
{
id: $scope.id,
}).then(
function (response) // success callback
{
window.location = '#Url.Action("PdfCreator", "someController")?Id=' + $scope.id;
window.location='#Url.Action("Index","AnotherController")';
},
function (response) // failure callback
{
alert(response.statusText);
});
Hi, I guess I am doing somehting wrong, I want to call to a function the sends me a file as a response, and afterwords I want to leave the page and go somewhere else.
the problem is, because this is a sync I don't get my download.
How can I make this synced?

Async has nothing to do with it. Once you're inside the success callback, the async part is already done. The problem is that you're changing the window location again before the first change has had time to load. In other words, it's the exact opposite of an async problem; the problem is that this code is synchronous and runs too fast.
However, the approach here is flawed to begin with. It might work if the browser was forced to download the file, as the then the first change to window.location would not itself cause the browser view to change. Since PDF is typically a browser-viewable type, this is not guaranteed, though. Regardless, you still have the same issue of need to delay the second call until the first has gotten a response, which is basically impossible. There's no built in event for this type of thing, so the best you'd could do would be to is use setTimeout with a 1-2 second delay, and just hope that that is enough time to get the first response. Even then, if it ever took longer, your code would break again. In other words, it's going to be extremely brittle.
The simple fact is that this is just simply not how HTTP works. You're basically trying to return two responses for a single request, which is not possible. This is a clever way to try to skirt around inherent restrictions in the protocol, I'll give you that, but it's ultimately still insufficient.
All that said, you can actually make this happen via the HTML5 File API and AJAX, but your solution then will only be compatible with modern browsers (basically everything except IE 10 and under). If you do not need to support lesser versions of IE, then you can use the following code instead:
function (response) // success callback
{
$http.get('#Url.Action("PdfCreator", "someController")?Id=' + $scope.id').then(
function (response) // success callback
{
var a = document.createElement('a');
var url = window.URL.createObjectURL(response.data);
a.href = url;
a.download = 'myfile.pdf';
a.click();
window.URL.revokeObjectURL(url);
window.location = '#Url.Action("Index","AnotherController")';
},
function (response) // failure callback
{
alert(response.statusText);
}
);
},
The secret sauce is in fetching the PDF via AJAX and then creating an object URL out of the PDF data. You can then use that to create an anchor element in the DOM and "click" it dynamically to prompt the download. The caveat, though, is that I haven't tried to do this with Angular, so I'm unsure if $http supports getting a binary response. I know with jQuery, you just have to tell it that the XHR object's response type is 'blob', but I'm not sure if you can or how you would do the same thing with Angular. As an alternative, you can simply use XMLHttpRequest directly for this particular AJAX, and simply set xhr.responseType = 'blob'.

Related

First ajax call goes extremely slow, subsequent calls run quickly -- why?

I'm using a simple jQuery AJAX function that runs extremely slow (10-15 seconds) the first time it's called, and then runs normally at <1 - 2 seconds each time it's called after that first time. I cannot figure out why this is happening but need to speed it up as much as possible. Here is the function:
function getNewItemAlt(apiUrl, callType, apiKey, dataType, returnValue, appendToWrapper) {
// ajax call to the api
return $.ajax({
type: callType,
url: apiUrl,
data: apiKey,
dataType: dataType,
success: function(result) {
appendToWrapper.closest('.game_play_area').find('.game_loader').remove();
// this is the thing that we want (probably either
// an image url or an actual value)
var desiredReturn = deepValue(result, returnValue);
var specialClass = '';
console.log(typeof desiredReturn)
if (typeof desiredReturn === 'number') {
specialClass = 'number'
}
// if it's a URL then it's an image and can be setup
// in an imgage tag and added to the dom
if (desiredReturn.toString().substring(0, 4) == "http") {
$(appendToWrapper).children('.game_image').remove();
$(appendToWrapper).prepend('<img class="game_image" src="' + desiredReturn + '" />');
} else {
$(appendToWrapper).children('.game_value_return').remove();
$(appendToWrapper).prepend('<p class="game_value_return ' + specialClass + '">' + desiredReturn + '</p>');
}
// clear the space to play the game
// $(currentGameWrapper).children('.game_intro').remove();
// show the game
// currentGameWrapper.children('.game_play_area').removeClass('hide');
},
error: function(err) {
console.log(err);
}
});
}
An example of an API that I'm making a request to is the Giphy API. I'm not convinced this is a server issue because it happens only on the first call to the api and then the subsequent calls are speedy.
Any ideas why this is happening and what can be done to make this run faster?
Considering the whole issue Javascript (client side) + API (server side) could complicate diagnosing the issue, so my suggestion to get a more specific answer would be to isolate the issue first.
Answering your general question, Reasons why?: It could be a lot of things but the remarkable ones are:
Handshake: the first interaction between your page and the server makes the remote server to authenticate you and validate your session. Later calls wont go through that process.
Server first execution: (less probable if you are using public APIs) if you are using a remote server with Java for example, that you are restarting, the first time you call a service it will load the instances, but for future calls those instances are already created hence they respond faster.
Network: (I don't think so... but...) trace your HTTP request to see how many jumps it has and how much is taking for them to be resolved.
How to Diagnose (isolation): Measure the time each step takes, it could be a simple print of your current time. I would break it the in the following steps:
Preparing the call to the API.
Calling the API.
Getting the data.
Manipulate the received data on the client side.
NOTE: steps 2 and 3 could go together.
How to mitigate this from happening (it doesn't solve the issue, but mitigates it):
Handshake: if the issue is related with authentication/authorization I recommend you to do an empty pre-fetch (without requesting any data) to deal with the handshake. Then you do a data fetch when you need it without that overhead.
Server first execution: you don't have too much to do here unless you own the server also. In this case I recommend also a pre-fetch but calling the entire service to initialize the server objects.
Javascript API: if the problem is dealing with the data on your client side then review how to optimize your Javascript code.
This might be a long shot. "appendToWrapper" is an object passed in by reference. If it's taking a long time to resolve (ie it takes 10 seconds or so to find ".game_play_area" etc... in the DOM) then it would be slow the first time, but saved in the calling function and fast subsequent times.
It might be easy to check. If you could make a deep copy of the object before passing it in, we would expect the AJAX to be slow every time, not just the first time. If that's the case, you'd need to clean up your selectors to make them faster. Perhaps use ids instead of classes.

JavaScript img.src onerror event - get reason of error

There can be different reasons for <img> load errors, such as network error response, bad image data...
error object received from onerror doesn't seems to specify the exact reason.
Is there a way to know if the error is because of a network error, say HTTP 500 or a network timeout?
EDIT:
I'm not looking for an alternative way to load a resource, such as AJAX request. I need an answer specifically for <img> tag with onerror event. The reason for that is that I'm using this method for pixel-tracking and I need a way to retry on upon network errors. I'm also not looking for alternative tracking methods such as JSONP.
Edit 16Nov16 2020GMT
Maybe you are pixel-tracking in emails or other clients limited in Javascript capabilities.
One idea that comes to mind is to use URL query paramters in your <img>'s src URL.
With regards to network timeouts, I will pose the idea that a user opens an email, loads the email entirely, then disconnects from the internet and somehow this does not give the tracker enough time to load.
https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout
I would suggest to use setTimeout() inside your onerror function.
This will continue attempting to set/load the <img>'s src URL. You could append the seconds it took until successful load to the URL of your src file as a query parameter like ?s=<sec>
As far as determining status 500 codes on image loads you might want to consider creating a custom 500 error file which would then create -- for example -- a MySQL database entry with all sorts of information you have access to and if you chose to use the query parameters mentioned before then you have slightly more information added to the error.
onerror for <img> gives limited information about the network
The information that is available from <img> can be found at
https://www.w3.org/TR/html/semantics-embedded-content.html#htmlimageelement-htmlimageelement
Older answer:
Perhaps a route you would like to try is to use AJAX to load the image data and set the <img> src to the base64 of the image data received. I hope this helps.
Edit 14Nov16 2018GMT
Alternatively use AJAX to determine if the image loads properly and then use the same URL sent to AJAX as the src for your <img>. It would of course be redundant but would avoid the issue of long "data" URLs.
Edit 15Nov16 0832GMT
Also regarding Network Timeout I found this thread to be useful JQuery Ajax - How to Detect Network Connection error when making Ajax call
Apparently you can specify a timeout to AJAX much like using error except you just provide the miliseconds manually.
Converting to Base64
https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding
https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/btoa
var encodedData = window.btoa("Hello, world"); // encode a string
Or if you are concerened about older browsers able to use btoa() then you might be interested in Google's https://chromium.googlesource.com/chromiumos/platform/spigots/+/refs/heads/firmware-u-boot-v1/base64_encode.js
Status Code checks in jQuery's AJAX
jQuery: How to get the HTTP status code from within the $.ajax.error method?
$.ajax({
type: 'GET',
url: '/path-to-my/image.png',
data: null,
success: function(data){
alert('horray! 200 status code!');
// convert to base64; add to img.src # btoa(data)
document.querySelector("#hlogo img").src = "data:;base64,"+ data;
},
error:function (xhr, ajaxOptions, thrownError){
switch (xhr.status) {
case 400:
// Take action, referencing xhr.responseText as needed.
case 404:
// Take action, referencing xhr.responseText as needed.
case 500:
// Take action, referencing xhr.responseText as needed.
}
});
Notes
https://www.rfc-editor.org/rfc/rfc2397#section-3
dataurl := "data:" [ mediatype ] [ ";base64" ] "," data
mediatype := [ type "/" subtype ] *( ";" parameter )
data := *urlchar
parameter := attribute "=" value
https://www.rfc-editor.org/rfc/rfc2046#section-4.2
Using of a generic-purpose image viewing application this way
inherits the security problems of the most dangerous type supported
by the application.
https://www.rfc-editor.org/rfc/rfc2397#page-4
The effect of using long "data" URLs in applications is currently
unknown; some software packages may exhibit unreasonable behavior
when confronted with data that exceeds its allocated buffer size.
Other References
Unknown file type MIME?
Asynchronously load images with jQuery

issue with JSONP getting a cached response from a WCF service

I use JSONP on a client to get data from a server using a WCF service that can return results using HTTP GET (It gets a 'callback' parameter which is a 'function name' and returns callback({data}), you know... JSONP).
Everything works, but if I enable caching (using 'AspNetCacheProfile')on one of the service's operations - then something is wrong, and I'm not sure what...
The way I get the JSONP is by using a function I picked up some time ago from a question here on SO (http://stackoverflow.com/questions/2499567/how-to-make-a-json-call-to-a-url)
function getJSONP(url, success) {
var ud = 'fc_' + + Math.floor(Math.random()*1000000),
script = document.createElement('script'),
head = document.getElementsByTagName('head')[0]
|| document.documentElement;
window[ud] = function(data) {
head.removeChild(script);
success && success(data);
};
script.src = url.replace('callback=?', 'callback=' + ud);
head.appendChild(script);
}
This creates a random id ('fc_xxxx') then assigns it as a function inside the window object, then use it as the 'callback' parameter for the url of the dynamic javascript that is injected to the document, and then the 'ud' function runs, removes the script and calls the 'success' callback function with the received data.
When using it on normal uncached operations from the service, it usually takes about 200ms to get back the response, and it works ok. The cached responses takes ~10ms -
and I get an error that the 'fc_XXXXX' function is undefined.
It's as if the response is "too fast" for it.
I also tried using jQuery.getJSON() - and, again the callback doesn't trigger.
In all cases when I look at the network traffic in Firebug - I can see the GET request, and I can see that the right data does in fact gets returned.
Does anybody have an idea how can I make it work right with the cached responses...?
I got it! The name of the response-function is different on each call (on both my manual jsonp implementation and that of jQuery).
The name of the function is part of the response from the server (as that's part of how jsonp works...).
So, if the response is a cached response - it will actually return the old name of the function (which will no longer exist on the client's context).
So I just need to give a constant name for the callback function in this case and it should bee fine. :)

Make Ajax Request Recursive - Checks if File Exists

Basically just looking to see if a particular txt file exists on the server, and if so, do further processing; however, I don't think my recursion is correct, so can someone offer a few pointers - here's what I have:
function fileExists(filename) {
$.ajax({
type: 'HEAD',
url: 'http://www.example.com/system/'+filename+'.txt',
success: function() {
// Further processing if file exists
},
error: function() {
// File does not exists, run through function again-
return arguments.callee(filename);
}
});
}
It's pretty basic, there's some processing before hand that actually creates the file; however the issue is it's FTP-ed up to our domain, which means timing can vary by a few seconds, so basically I just want it to recheck until it sees that the file exists. I'll modify it a little afterwards to control the stack, possibly setting a timeout of half a second or something, but I'm not that great with javascript, so I need a few pointers to make this recursive. Any help is GREATLY appreciated.
the issue is when you try to call fileExists again via arguments.callee(fileName), the scope of the error method isn't what you think it is.
Just call fileExists.
The other you are going to have is that if your server is quick, you are going to be firing a ton of requests. You probably want to wait some time between requests. So make error contain
setTimeout(function(){
console.log('trying again....'); // this won't work in IE, I *think*
fileExists(filename);
}, 1000); // try again in a second
Finally, you should realize that the error callback only gets invoked if the server returns a 500. The 500 code usually means there was an error on your server. If a file doesn't exist, you should probably return json to indicate the file doesn't exist, and handle that case in your success callback.
error: function() {
fileExists(filename);
}

How to obtain jquery post return values?

I want to retrieve the height and width of an image on a server by using an ajax post call to a php file which returns a double pipe delimited string 'width||height'
My javascript is correctly alerting that requested string from the php file so the info is now in my script but i cannot seem to access it outside the $.post function.
This works:
var getImagesize = function(sFilename)
{
$.post("getImagesize.php", { filename: sFilename, time: "2pm" },
function(data){
alert(data.split('||'));
});
}
But retrieving is a different matter:
// this line calls the function in a loop through images:
var aOrgdimensions = getImagesize($(this, x).attr('src')) ;
alert(aOrgdimension);
// the called function now looks like this:
var getImagesize = function(sFilename)
{
var aImagedims = new Array();
$.post("getImagesize.php", { filename: sFilename },
function(data){
aImagedims = data.split('||');
});
return "here it is" + aImagedims ;
}
Anyone able to tell me what i'm doing wrong?
You are misunderstanding the way that an AJAX call works. The first "A" in AJAX stands for asynchronous, which means that a request is made independent of the code thread you are running. That is the reason that callbacks are so big when it comes to AJAX, as you don't know when something is done until it is done. Your code, in the meantime, happily continues on.
In your code, you are trying to assign a variable, aOrgdimensions a value that you will not know until the request is done. There are two solutions to this:
Modify your logic to reconcile the concept of callbacks and perform your actions once the request is done with.
Less preferably, make your request synchronous. This means the code and page will "hang" at the point of the request and only proceed once it is over. This is done by adding async: false to the jQuery options.
Thanx for the Asynchronous explaination. I did not realize that, but at least now i know why my vars aren't available.
Edit: Figured it out. Used the callback function as suggested, and all is well. :D

Categories

Resources