I have a script that requires quite a few seconds of processing, up to about minute. The script resizes an array of images, sharpens them and finally zips them up for the user to download.
Now I need some sort of progress messages.
I was thinking that with jQuery's .post() method the data from the callback function would progressively update, but that doesn't seem to work.
In my example I am just using a loop to simulate my script:
$(document).ready(function() {
$('a.loop').click(function() {
$.post('loop.php', {foo:"bar"},
function(data) {
$("div").html(data);
});
return false;
});
});
loop.php:
for ($i = 0; $i <= 100; $i++) {
echo $i . "<br />";
}
echo "done";
Update: Getting progress information is a lot easier since jQuery Ajax requests have a promise interface. Use this answer:
https://stackoverflow.com/a/32272660/18771
The original answer below is outdated (it is originally from 2010). It still works but is more complicated than it needs to be. I'll keep it in place for reference and and comparison.
You need some kind of progress info from the server. The ajax callbacks do no progressive work, they fire just once - after the request returned successfully.
So... in PHP you would need something like this:
/* progress.php */
$batch_done = some_way_to_find_out_that_number();
$batch_size = some_way_to_find_out_that_number_too();
header('Content-type: application/json');
// TODO: format number
echo '{"progress":'. ($batch_size==0 ? '0' : $batch_done*100.0/$batch_size).'}';
For this to work your image processing script must leave some evidence of its progress of course.
And in JavaScript something like this:
$(document).ready(function() {
$('a.loop').click(function() {
var queryData = {foo:"bar"};
// prepare a function that does cyclic progress checking
var progressCheck = function() {
$.getJSON(
"progress.php", queryData,
function(data) {
$("div.progress").css("width", data.progress+"%");
}
)
};
$.post(
'loop.php', queryData,
/* create the post request callback now */
function(intvalId){
return function(data) {
$("div").html(data);
clearInterval(intvalId);
}
}( setInterval(progressCheck, 1000) )
);
return false;
});
});
This part requires some explanation:
function(intvalId){
return function(data) {
$("div").html(data);
clearInterval(intvalId);
};
}( setInterval(progressCheck, 1000) )
function(intvalId) is an anonymous function that takes one argument - an interval ID. This ID is necessary to stop an interval that has been set up via setInterval(). Luckily, the call to setInterval() returns this very ID.
The anonymous outer function returns an inner function(data), this one will be the actual callback for $.post().
We call the outer function immediately, doing two things in the process: Triggering off the interval with setInterval() and passing in its return value (the ID) as an argument. This argument value will be available to the inner function at its call time (which may be some minutes in the future). The callback for post() now can actually stop the interval.
As an exercise for you ;)
post() cannot be triggered twice inadvertently.Thanks to Tomalak I finally put something together that works. Since I am not actually writing my image files on the server when processing the batch I wrote a log file that I am consulting in the progress.php script.
I would like to know if this is the best way of doing this. I wanted to avoid writing to a file and tried with PHP's $_session but cannot seem to progressively read from it. Is this possible with $_session?
HTML:
<a class="download" href="#">request download</a>
<p class="message"></p>
JS:
$('a.download').click(function() {
var queryData = {images : ["001.jpg", "002.jpg", "003.jpg"]};
var progressCheck = function() {
$.get("progress.php",
function(data) {
$("p.message").html(data);
}
);
};
$.post('proccess.php', queryData,
function(intvalId) {
return function(data) {
$("p.message").html(data);
clearInterval(intvalId);
}
} (setInterval(progressCheck, 1000))
);
return false;
});
process.php:
$arr = $_POST['images'];
$arr_cnt = count($arr);
$filename = "log.txt";
$i = 1;
foreach ($arr as $val) {
$content = "processing $val ($i/$arr_cnt)";
$handle = fopen($filename, 'w');
fwrite($handle, $content);
fclose($handle);
$i++;
sleep(3); // to mimic image processing
}
echo "<a href='#'>download zip</a>";
progress.php:
$filename = "log.txt";
$handle = fopen($filename, "r");
$contents = fread($handle, filesize($filename));
fclose($handle);
echo $contents;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With