Upload Progress - Sometimes $_SESSION[$key] is Empty - javascript

I have Ubuntu 12.04 LTS and using PHP 5.5 with Apache2 to implement the upload progress via the PHP session upload progress.
The issue is that it works sometimes and sometimes it doesn't work. I mean sometimes I get the progress percentage 100% direct at the beginning of the upload without finishing the upload (which means the $_SESSION[$key] is empty in that cases, but why?!)
I tried turning the value of session.upload_progress.cleanup to On and Off, but it didn't change anything.
You can try it yourself on this URL: http://138.128.124.172/upload_progress
In the php.ini, I have the below settings related to the upload:
;;;;;;;;;;;;;;;;
; File Uploads ;
;;;;;;;;;;;;;;;;
; Whether to allow HTTP file uploads.
; http://php.net/file-uploads
file_uploads = On
; Temporary directory for HTTP uploaded files (will use system default if not
; specified).
; http://php.net/upload-tmp-dir
;upload_tmp_dir =
; Maximum allowed size for uploaded files.
; http://php.net/upload-max-filesize
upload_max_filesize = 100M
; Maximum number of files that can be uploaded via a single request
max_file_uploads = 20
; Enable upload progress tracking in $_SESSION
; Default Value: On
; Development Value: On
; Production Value: On
; http://php.net/session.upload-progress.enabled
session.upload_progress.enabled = On
; Cleanup the progress information as soon as all POST data has been read
; (i.e. upload completed).
; Default Value: On
; Development Value: On
; Production Value: On
; http://php.net/session.upload-progress.cleanup
session.upload_progress.cleanup = Off
; A prefix used for the upload progress key in $_SESSION
; Default Value: "upload_progress_"
; Development Value: "upload_progress_"
; Production Value: "upload_progress_"
; http://php.net/session.upload-progress.prefix
;session.upload_progress.prefix = "upload_progress_"
; The index name (concatenated with the prefix) in $_SESSION
; containing the upload progress information
; Default Value: "PHP_SESSION_UPLOAD_PROGRESS"
; Development Value: "PHP_SESSION_UPLOAD_PROGRESS"
; Production Value: "PHP_SESSION_UPLOAD_PROGRESS"
; http://php.net/session.upload-progress.name
;session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS"
; How frequently the upload progress should be updated.
; Given either in percentages (per-file), or in bytes
; Default Value: "1%"
; Development Value: "1%"
; Production Value: "1%"
; http://php.net/session.upload-progress.freq
;session.upload_progress.freq = "1%"
; The minimum delay between updates, in seconds
; Default Value: 1
; Development Value: 1
; Production Value: 1
; http://php.net/session.upload-progress.min-freq
;session.upload_progress.min_freq = "1"
At the PHP side: I have the below code inside the page: progress.php:
session_start();
$key = ini_get("session.upload_progress.prefix") . "myForm";
if (!empty($_SESSION[$key])) {
$current = $_SESSION[$key]["bytes_processed"];
$total = $_SESSION[$key]["content_length"];
echo $current < $total ? ceil($current / $total * 100) : 100;
}
else {
echo 100;
}
At the client side, I have the below code in the page index.php
<?php
if ($_SERVER["REQUEST_METHOD"] == "POST" && !empty($_FILES["userfile"])) {
// move_uploaded_file()
}
?>
<style>
#bar_blank {
border: solid 1px #000;
height: 20px;
width: 300px;
}
#bar_color {
background-color: #006666;
height: 20px;
width: 0px;
}
#bar_blank, #hidden_iframe {
display: none;
}
</style>
<html>
<head>
<title>File Upload Progress Bar</title>
</head>
<body>
<div id="bar_blank">
<div id="bar_color"></div>
</div>
<div id="status"></div>
<form action="<?php echo $_SERVER["PHP_SELF"]; ?>" method="POST" id="myForm" enctype="multipart/form-data" target="hidden_iframe">
<input type="hidden" value="myForm" name="<?php echo ini_get("session.upload_progress.name"); ?>">
<input type="file" name="userfile"><br>
<input type="submit" value="Start Upload">
</form>
<iframe id="hidden_iframe" name="hidden_iframe" src="about:blank"></iframe>
</body>
</html>
<script>
function toggleBarVisibility() {
var e = document.getElementById("bar_blank");
e.style.display = (e.style.display == "block") ? "none" : "block";
}
function createRequestObject() {
var http;
if (navigator.appName == "Microsoft Internet Explorer") {
http = new ActiveXObject("Microsoft.XMLHTTP");
}
else {
http = new XMLHttpRequest();
}
return http;
}
function sendRequest() {
var http = createRequestObject();
http.open("GET", "progress.php");
http.onreadystatechange = function () { handleResponse(http) };
http.send(null);
}
function handleResponse(http) {
var response;
if (http.readyState == 4) {
response = http.responseText; //alert(response);return;
document.getElementById("bar_color").style.width = response + "%";
document.getElementById("status").innerHTML = response + "%";
if (response < 100) {
setTimeout("sendRequest()", 1000);
}
else {
toggleBarVisibility();
document.getElementById("status").innerHTML = "Done.";
document.getElementById("bar_color").style.width = 0 + "%";
}
}
}
function startUpload() {
toggleBarVisibility();
setTimeout("sendRequest()", 1000);
}
(function () {
document.getElementById("myForm").onsubmit = startUpload;
})();
</script>
I am not interested in the HTML5, Jquery or the flash. I would be thankful if you hint me also about better approaches to get a robust way to implement the upload with a progress bar.
Thanks for your help!

I use the reply part due to size of the answer. Or, size of some details...
In fact I've have the same problem, with PHP 5.5.18 running on Debian Whezzy.
After making a few test and putting a log in the progress.php in order to save the value of the $key, bytes_processed and content_length, here are my conclusions:
Discovery 1: we don't have an empty key. We have a key showing us informations with bytes_processed = content_length
Discovery 2: if you download eg 4 files with different size and then have a look at the log of your progress.php you'll see the value from the session for second file will give you the result for file 1.
Example:
Send test.docx -> 500.000 bytes. $key is empty
Send house.jpg -> 4.000.000 bytes. $key give bytes_processed = content_length = 500.000 so result of previous file
In many case, we use in the form, an hidden field like this:
echo "<input type=hidden value=\"myForm\" name=\"";
echo ini_get("session.upload_progress.name");
echo "\" />\n";
And we get the data using in progress.php:
$key = ini_get("session.upload_progress.prefix") . "myForm";
meaning ALL our $key have the same name.
I change by:
$val = time();
echo "<input type=hidden value=\"".$val."\" name=\"";
echo ini_get("session.upload_progress.name");
echo "\" />\n";
and
$key = ini_get("session.upload_progress.prefix") . $_POST[ini_get("session.upload_progress.name")];
Now, each time I have an empty key.
My conclusion is that we have a cache problem which is what PHP.net say:
Warning
The web server's request buffering has to be disabled for this to work properly, else PHP may see the file upload only once fully uploaded. Servers such as Nginx are known to buffer larger requests.

Older post, but I'd suggest two things:
Make hidden field dynamic value
$_SESSION['ukey'] = substr(md5(uniqid(rand(), true)),0,6);
<input type="hidden" value="<?php echo $_SESSION['ukey'] ?>" name="<?php echo ini_get("session.upload_progress.name"); ?>">
By this you achieve that you can send same filenames again and it will work, you will get unique session id. Also you can use in php.ini the value session.upload_progress.cleanup = Off so data in session will be there after 100% is reached. In progres.php change to $key = ini_get("session.upload_progress.prefix") . echo $_SESSION['ukey'];
In progress script also this part of the code causing trouble:
else {
echo 100;
}
The reason is that there might be buffering in some intermediate device or apache or in the transit, so $_SESSION[$key] will be initialised even after browser already send all 100% of POST data. This is my scenario at certain ISPs. I removed this code and it is working fine. By this you achieve that AJAX will be pooling data always and not hang on this. You only need to handle exception when TCP would for some reason dropped and AJAX would be trying pooling endlessly until you close browser. But I don't know how often this would happen / if it happens.

Related

Cache problem? Server side events work in localhost, not in production enviroment

I want to ask this question with a simply example. (I will write down at the end of the post).
I have read this:
server sent events not updating until script is finished
But I don't know how to solve it.
With the solution of its answer (https://stackoverflow.com/a/37690766/8494053) works perfect, so may be it is a problem of cache in my production server (share web hosting).
I recieve all the EventStream at the end, all in a row at the same time.
I have already checked all combinations of:
header('Cache-Control: no-cache, no-store, must-revalidate, private, max-age=0');
header('Pragma: no-cache');
header('Expires: 0');
But no luck
Any one knows how to solve this without "str_pad($message, 800000)" ?
Any clue to compare my localhost configuration of the server and the shared hostweb server?
Thanks,
NOTE 1: php version 8 in both enviroments. I have checked that I work with apached as developement enviroment and CGI/FastCGI in my shared webserver. Is it related?
I have found this:
Event Source -> Server returns event stream in bulk rather then returning in chunk
NOTE 2: Output buffering is the same in both servers: output_buffering 4096
This is a simple example that doesnt work in my hosting:
test.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<br />
<input type="button" onclick="startTask();" value="Start Long Task" />
<input type="button" onclick="stopTask();" value="Stop Task" />
<br />
<br />
<p>Results</p>
<br />
<div id="results" style="border:1px solid #000; padding:10px; width:300px; height:250px; overflow:auto; background:#eee;"></div>
<br />
<progress id='progressor' value="0" max='100' ></progress>
<span id="percentage" style="text-align:right; display:block; margin-top:5px;">0</span>
</body>
</html>
<script>
var es;
function startTask() {
if (!!window.EventSource) {
es = new EventSource('long_process.php');
//a message is received
es.addEventListener('message', function(e) {
var result = JSON.parse( e.data );
addLog(result.message);
if(e.lastEventId == 'CLOSE') {
addLog('Received CLOSE closing');
es.close();
var pBar = document.getElementById('progressor');
pBar.value = pBar.max; //max out the progress bar
}
else {
var pBar = document.getElementById('progressor');
pBar.value = result.progress;
var perc = document.getElementById('percentage');
perc.innerHTML = result.progress + "%";
perc.style.width = (Math.floor(pBar.clientWidth * (result.progress/100)) + 15) + 'px';
}
});
es.addEventListener('error', function(e) {
addLog('Error occurred');
es.close();
});
}
}
function stopTask() {
es.close();
addLog('Interrupted');
}
function addLog(message) {
var r = document.getElementById('results');
r.innerHTML += message + '<br>';
r.scrollTop = r.scrollHeight;
}
</script>
long_process.php
<?php
header('Content-Type: text/event-stream');
// recommended to prevent caching of event data.
header('Cache-Control: no-cache');
function send_message($id, $message, $progress) {
$d = array('message' => $message , 'progress' => $progress);
echo "id: $id" . PHP_EOL;
echo "data: " . json_encode($d) . PHP_EOL;
echo PHP_EOL;
//push the data out by all force possible
ob_flush();
flush();
}
//LONG RUNNING TASK
for($i = 1; $i <= 10; $i++) {
send_message($i, 'on iteration ' . $i . ' of 10' , $i*10);
sleep(1);
}
send_message('CLOSE', 'Process complete', 100);
?>
UPDATE About #Tigger answer: I have used this code, but no luck. Again I recieve all in a row at the end of the script (10seconds), not a message every 1 second.
(I have also checked "\n" and PHP_EOL).
function send_message($id, $message, $progress) {
$d = array('message' => $message , 'progress' => $progress);
echo "id: $id" . "\n";
echo "data: " . json_encode($d) . "\n";
echo "\n";
//push the data out by all force possible
while(ob_get_level() > 0) {
ob_end_flush();
}
flush();
}
UPDATE About second #Tigger answer
I have used MDN sample on GitHub and no luck. XAMPP works, my production webserver ... doesn't.
UPDATE About hosting provider
As I have not found a solution, I have contacted with my shared web hosting, and here is their answer:
(translate with google):
Hello, After analyzing the case, as we have been able to verify, the use of SSE on a platform like ours with an nginx proxy ahead of apache, would require certain customizations in the nginx configuration of the hosting, which makes it incompatible with the service of shared hosting. You need a service that is more customizable such as a vps, or a virtual private server or similar. Greetings,
As I can't change nginx configuration, is it any other configuration/command in my php files or javascript that will help me?
After a lot of messing around I found the following syntax for your long_process.php works best in my environment.
My server is using FreeBSD and my PHP scripts (PHP 8) are also Unix formatted (important for line returns). If you are on a mix of Windows and Linux, your line returns could be part of the issue.
I also found ob_get_level() helped a lot. The connection_aborted() check will close off the script quicker too. This will prevent the script from continuing when the user navigates away, returning resources to the webserver.
My JavaScript structure is a bit different from yours as well, but your issue appears to be on the PHP side, so I have skipped that part.
long_process.php
// how long between each loop (in seconds)
define('RETRY',4);
header("Cache-Control: no-cache");
header("Content-Type: text/event-stream");
// skip the first check as the member just started
echo 'retry: '.(RETRY * 1000);
echo 'data: {"share":true,"update":false}';
echo "\n\n";
flush();
sleep(RETRY);
while(1) {
if (... some conditional check here ...) {
echo 'data: {"share":true,"update":true}';
} else {
echo 'data: {"share":true,"update":false}';
}
echo "\n\n";
while(ob_get_level() > 0) {
ob_end_flush();
}
flush();
if (connection_aborted()) {
break;
}
sleep(RETRY);
}
As per this answer on a similar question, this is an Nginx isssue. You can fix this by adding a 'X-Accel-Buffering' header with value 'no' in your response. See this entry in the Nginx documentation for more detail.

Get bytes transferred using PHP5 for POST request

Note I am new to PHP, Apache and programming for servers so more thorough explanations will be appreciated.
Context
I created - in javascript - a progress bar to display when a file is uploaded. Current I have the progress bar update at a set frame-rate (to see if it works).
Clearly to make this an accurate progress bar, everything should in relation to the number of bytes transferred in comparison to the total number of bytes.
Question
using PHP5 how can I get information regarding the number of bytes transferred in relation to the total number of bytes of the file, such that I can pass that to a JS function updateProgress(bytesSoFar, totalBytes) to update my progress bar? Please verbosely walk me through the modifications needed to the code below to get this to work. I have seen xhr examples, but they are not thoroughly accessible.
I have just set up LocalHost and am using W3Schools' PHP File Upload tutorial. To get the simulated ''upload'' to work, I changed the local permissions as suggested in this S.O. post. I don't necessarily need to read the file, I just want to know many bytes have been transferred.
Code
Currently I have two files:
index.php
upload.php
index.php
<!DOCTYPE html>
<html>
<body>
<form action="upload.php" method="post" enctype="multipart/form-data">
Select image to upload:
<input type="file" name="fileToUpload" id="fileToUpload">
<input type="submit" value="Upload Image" name="submit">
</form>
</body>
</html>
upload.php
<?php
$target_dir = "uploads/";
$target_file = $target_dir . basename($_FILES["fileToUpload"]["name"]);
$uploadOk = 1;
$imageFileType = pathinfo($target_file,PATHINFO_EXTENSION);
// Check if image file is a actual image or fake image
if(isset($_POST["submit"])) {
$check = getimagesize($_FILES["fileToUpload"]["tmp_name"]);
if($check !== false) {
echo "File is an image - " . $check["mime"] . ".";
$uploadOk = 1;
} else {
echo "File is not an image.";
$uploadOk = 0;
}
}
// Check if file already exists
if (file_exists($target_file)) {
echo "Sorry, file already exists.";
$uploadOk = 0;
}
// Check file size
if ($_FILES["fileToUpload"]["size"] > 500000) {
echo "Sorry, your file is too large.";
$uploadOk = 0;
}
// Allow certain file formats
if($imageFileType != "jpg" && $imageFileType != "png" && $imageFileType != "jpeg"
&& $imageFileType != "gif" ) {
echo "Sorry, only JPG, JPEG, PNG & GIF files are allowed.";
$uploadOk = 0;
}
// Check if $uploadOk is set to 0 by an error
if ($uploadOk == 0) {
echo "Sorry, your file was not uploaded.";
// if everything is ok, try to upload file
} else {
if (move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $target_file)) {
echo "The file ". basename( $_FILES["fileToUpload"]["name"]). " has been uploaded.";
} else {
echo "Sorry, there was an error uploading your file.";
}
}
?>
Update
I have found this code:
test.php
<?php
if ($_SERVER["REQUEST_METHOD"] == "POST" && !empty($_FILES["userfile"])) {
// move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $target_file)
move_uploaded_file($_FILES["userfile"]["tmp_name"], "uploads/" . $_FILES["userfile"]["name"]);
}
?>
<html>
<head>
<title>File Upload Progress Bar</title>
<style type="text/css">
#bar_blank {
border: solid 1px #000;
height: 20px;
width: 300px;
}
#bar_color {
background-color: #006666;
height: 20px;
width: 0px;
}
#bar_blank, #hidden_iframe {
display: none;
}
</style>
</head>
<body>
<div id="bar_blank">
<div id="bar_color"></div>
</div>
<div id="status"></div>
<form action="<?php echo $_SERVER["PHP_SELF"]; ?>" method="POST" id="myForm" enctype="multipart/form-data" target="hidden_iframe">
<input type="hidden" value="myForm" name="<?php echo ini_get("session.upload_progress.name"); ?>">
<input type="file" name="userfile"><br>
<input type="submit" value="Start Upload">
</form>
<script type="text/javascript">
function toggleBarVisibility() {
var e = document.getElementById("bar_blank");
e.style.display = (e.style.display == "block") ? "none" : "block";
}
function createRequestObject() {
var http;
if (navigator.appName == "Microsoft Internet Explorer") {
http = new ActiveXObject("Microsoft.XMLHTTP");
}
else {
http = new XMLHttpRequest();
}
return http;
}
function sendRequest() {
var http = createRequestObject();
http.open("GET", "progress.php");
http.onreadystatechange = function () { handleResponse(http); };
http.send(null);
}
function handleResponse(http) {
var response;
if (http.readyState == 4) {
response = http.responseText;
document.getElementById("bar_color").style.width = response + "%";
document.getElementById("status").innerHTML = response + "%";
if (response < 100) {
setTimeout("sendRequest()", 1000);
}
else {
toggleBarVisibility();
document.getElementById("status").innerHTML = "Done.";
}
}
}
function startUpload() {
toggleBarVisibility();
setTimeout("sendRequest()", 1000);
}
(function () {
document.getElementById("myForm").onsubmit = startUpload;
})();
</script>
</body>
</html>
progress.php
session_start();
$key = ini_get("session.upload_progress.prefix") . "myForm";
if (!empty($_SESSION[$key])) {
$current = $_SESSION[$key]["bytes_processed"];
$total = $_SESSION[$key]["content_length"];
echo $current < $total ? ceil($current / $total * 100) : 100;
$message = ceil($current / $total * 100) : 100;
$message = "$message"
echo "<script type='text/javascript'>alert('$message');</script>";
}
else {
echo 100;
}
?>
Which, like my previous code, transfers the file. However, this doesn't show the bytes until the end (even though it should alert for that), also it opens a new window with the "Done." statement in the previous window.
You can check out this Php File Upload Progress Bar that may help you get started in case you insist on using Php to display progress. This uses the PECL extension APC to get uploaded file progress details. It is possible to calculate the number of bytes received by the server using the response of
apc_fetch() as per the first link.
Another interesting Track Upload Progress tutorial that uses Php's native Session Upload Progress feature.
Lastly, if you are a little open to using Javascript (or a JS library), that would be ideal. An easy to use, easy to setup, well known and a maintained library that I know of is FineUploader
You can use the Session Upload Progress feature. It's native but require some php.ini configuration.
Please, take a look at the PHP manual.
UPDATE 1
Note, you don't need to change your upload.php. You can create a new php file (ex. progress.php) and make requests to them to check the upload progress with your JS.
progress.php
<?php
$upload_id = $_GET['upload_id];
print json_encode($_SESSION['upload_progress_'.$upload_id]);
Then your JS will make a GET request to progress.php?upload_id=YourIdValue and you will receive a JSON with all progress information.

jQuery and perl: progress bar based on state of "pipeline file", dynamic ajax

I would like to build a small pipeline that allows the user to choose a file and then runs several scripts using this file as an input. As some of these scripts run for several minutes (exact time depends on the input file's size) I would like to show a progress bar that is based on how many scripts of this pipeline have been finished.
The problem is that I don't know how to update this progress bar based on the status of the pipeline and would appreciate some help with that. I show the files I use first and then explain the problem in more detail.
My html form:
<form action="main.pl" method="post" enctype="multipart/form-data">
<input type="file" name="fileName" />
<input type="submit" value="Analyze" />
</form>
The pipeline script main.pl:
#!/usr/bin/perl
use CGI;
use strict;
#use warnings;
my $q = CGI->new;
my $fileName = $q->param('fileName');
my $progressPerc = 0;
my $numJobs = 3; #in actual script much more
my $count = 1;
system('perl', './file1.pl', $fileName);
$progressPerc = $count/$numJobs*100;
#here I want to pass $progressPerc to the progress bar
$count += 1;
system('perl', './file2.pl', $fileName);
$progressPerc = $count/$numJobs*100;
#here I want to pass $progressPerc to the progress bar
$count += 1;
system('perl', './file3.pl', $fileName);
$progressPerc = $count/$numJobs*100;
#here I want to pass $progressPerc to the progress bar
I found a good working progress bar on http://jqueryui.com/progressbar/#label which looks as follows (I post the entire file although I would only need the .js-part):
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>http://jqueryui.com/progressbar/#label</title>
<link rel="stylesheet" href="http://code.jquery.com/ui/1.11.2/themes/smoothness/jquery-ui.css">
<script src="http://code.jquery.com/jquery-1.10.2.js"></script>
<script src="http://code.jquery.com/ui/1.11.2/jquery-ui.js"></script>
<style>
.ui-progressbar {
position: relative;
}
.progress-label {
position: absolute;
left: 50%;
top: 4px;
font-weight: bold;
text-shadow: 1px 1px 0 #fff;
}
</style>
<script>
$(function() {
var progressbar = $( "#progressbar" ),
progressLabel = $( ".progress-label" );
progressbar.progressbar({
value: false,
change: function() {
progressLabel.text( progressbar.progressbar( "value" ) + "%" );
},
complete: function() {
progressLabel.text( "Complete!" );
}
});
function progress() {
var val = progressbar.progressbar( "value" ) || 0;
progressbar.progressbar( "value", val + 10 );
if ( val < 99 ) {
setTimeout( progress, 800 );
}
}
setTimeout( progress, 2000 );
});
</script>
</head>
<body>
<div id="progressbar"><div class="progress-label">Loading...</div></div>
</body>
</html>
Is there any easy way to pass $progressPerc from main.pl to the function progress() every time it changes its value? If there was only one call, this could be done using ajax, however, I don't know how to use ajax for several calls i.e. dynamically; by 'dynamic' I mean that once a perl script in main.pl is finished, this should be reported to the progress bar which is then updated.
If there is no easy way to do this: Can one somehow introduce an if clause that checks every x minutes (using setTimeout) whether the output files produced by these perl scripts in main.pl exist and if so, the progress bar is updated and if not one waits for longer? And if so, how would it be implemented?
Almost a month has passed since I asked this question but an answer did not appear. Therefore, I now post my one which is based on ThisSuitIsBlackNot's comment.
Although not that elaborated, it might serve as a minimal example on how one can connect Perl, HTML, Javascript/Ajax and JSON. Maybe it helps someone to get started with that topic.
If you want to run this code, just copy index.html file to your html directory (e.g. /var/www/html) and the perl scripts to your cgi-bin directory (e.g. /var/www/cgi-bin). Make sure to make these perl scripts executable! In my code below, the cgi directory is in /var/www/cgi-bin/ajax/stackCGI - please change that accordingly.
The status of the pipeline is written to a file which is then read in in 1 second intervals, the progress bar is updated and a message about the current status is displayed. The duration that the single steps in the pipeline take, is represented by the Perl's sleep function.
The files are given below.
Any comments and improvements are welcome!
index.html:
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<meta charset='utf-8' />
<link rel="stylesheet" href="http://code.jquery.com/ui/1.11.3/themes/smoothness/jquery-ui.css">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://code.jquery.com/ui/1.11.3/jquery-ui.js"></script>
<style>
.ui-progressbar {
position: relative;
}
.progress-label {
position: absolute;
left: 50%;
font-weight: bold;
text-shadow: 1px 1px 0 #fff;
}
</style>
<script>
var progressVal = 0;
function get_progress() //get_progress();
{
$.ajax({
type: 'POST',
url: '/cgi-bin/ajax/stackCGI/readFromFileJson.pl',
success: function( res ) {
$('#progressPerc').append(' ' + res.progressSummary.progress);
$('#progressMessage').html('status of pipeline: ' + res.progressSummary.progressMessage);
$('.progress-label').html(res.progressSummary.progress + '%');
progressVal = parseFloat(res.progressSummary.progress);
$( "#progressbar" ).progressbar({
value: progressVal
});
}
});
if (progressVal < 100){ //pipeline has not finished yet
setTimeout(get_progress, 1000); //call the function each second every second to get a status update
}
else { //pipeline has finished
$('.progress-label').html('100%');
alert("pipeline has finished! your results can be found in path/to/files. an e-mail has been sent to user#provider.com");
}
}
function start_pipeline()
{
$.ajax({
type: 'POST',
url: '/cgi-bin/ajax/stackCGI/pipeline.pl',
data: { 'fileAnalysis': $('#myFile').val() },
success: function(res) {
//add your success function here
},
error: function() {alert("pipeline has not started!");}
});
}
</script>
</head>
<body>
file name: <input type='text' id='myFile'/>
<button onclick='start_pipeline();get_progress();' >Analyze now</button>
<div id="progressbar"><div class="progress-label"></div></div>
<div id="progressMessage"></div>
<div id="progressPerc">status of pipeline in percent (in this example the function get_progress is called every second): </div>
</body>
pipeline.pl:
#!/usr/bin/perl
use strict;
use warnings;
use CGI;
my $q = new CGI;
print $q->header('text/plain'); #needed! otherwise the ajax call in start_pipeline returns the error message
my $fileForAnalysis = $q -> param('fileAnalysis');
#create a file where the progress is reported to
#make sure you have the appropriate permissions to do this
my $filename = '/var/www/cgi-bin/ajax/stackCGI/progressReport.txt'; #change the directory!
my $fh; #file handler
my $number; #progress of pipeline in percent
my $message; #progress of pipeline
$number = 0;
$message = 'pipeline has startet successfully! Your file '.$fileForAnalysis.' is now processed.';
open($fh, '>', $filename) or die "Could not open file '$filename' $!";
print $fh $number."\t".$message;
close $fh;
sleep(3); #first program is running
$number = 10; #progress of pipeline in percent. as we have 4 programs in this pipeline it could also be 25 or whatever
$message = 'first program has finished';
open($fh, '>', $filename) or die "Could not open file '$filename' $!";
print $fh $number."\t".$message;
close $fh;
sleep(5); #second program is running
$number = 20;
$message = 'second program has finished';
open($fh, '>', $filename) or die "Could not open file '$filename' $!";
print $fh $number."\t".$message;
close $fh;
sleep(5); #third program is running
$number = 42;
$message = 'third program has finished';
open($fh, '>', $filename) or die "Could not open file '$filename' $!";
print $fh $number."\t".$message;
close $fh;
sleep(5); #fourth program is running
$number = 100;
$message = 'pipeline has finished';
open($fh, '>', $filename) or die "Could not open file '$filename' $!";
print $fh $number."\t".$message;
close $fh;
readFromFileJson.pl:
#!/usr/bin/perl
use strict;
use warnings;
use JSON;
use CGI;
my $q = new CGI;
#create a file where the progress is reported to
#make sure you have the appropriate permissions to do this
my $filename = '/var/www/cgi-bin/ajax/stackCGI/progressReport.txt'; #change the directory!
open(my $fh, '<:encoding(UTF-8)', $filename) or die "Could not open file '$filename' $!";
print $q->header('application/json;charset=UTF-8'); #output will be returned in JSON format
my #progressReport = split(/\t/,<$fh>); #file is tab separated
my %progressHash;
$progressHash{"progress"} = $progressReport[0];
$progressHash{"progressMessage"} = $progressReport[1];
#convert hash to JSON format
my $op = JSON -> new -> utf8 -> pretty(1);
my $output = $op -> encode({
progressSummary => \%progressHash
});
print $output;

Whenever I refresh my code, the query reloads and sends blank information to the database?

whenever I refresh the page or leave and come back, the image disappears from the site and the database. I dont want the form to redirect to a different page, I just want it to stay on the same page but only run the query when the submit button is pressed.
<?php
$display = mysql_query("SELECT image FROM `blog_users` WHERE username = '$session->username'");
$res = mysql_fetch_array($display); echo "<image style='height: 50px; width:50px;' src='".$res['image']."'>";
$close = 0;
?>
//the above code displays the image
<?php
define ("MAX_SIZE","1000");
//This function reads the extension of the file. It is used to determine if the
// file is an image by checking the extension.
function getExtension($str) {
$i = strrpos($str,".");
if (!$i) { return ""; }
$l = strlen($str) - $i;
$ext = substr($str,$i+1,$l);
return $ext;
}
//This variable is used as a flag. The value is initialized with 0 (meaning no
// error found)
//and it will be changed to 1 if an errro occures.
//If the error occures the file will not be uploaded.
$errors=0;
//checks if the form has been submitted
if(isset($_POST['Submit']))
{
//reads the name of the file the user submitted for uploading
$image=$_FILES['image']['name'];
//if it is not empty
if ($image)
{
//get the original name of the file from the clients machine
$filename = stripslashes($_FILES['image']['name']);
//get the extension of the file in a lower case format
$extension = getExtension($filename);
$extension = strtolower($extension);
//if it is not a known extension, we will suppose it is an error and
// will not upload the file,
//otherwise we will do more tests
if (($extension != "jpg") && ($extension != "jpeg") && ($extension !=
"png") && ($extension != "gif"))
{
//print error message
echo '<h3>Unknown extension!</h3>';
$errors=1;
}
else
{
//get the size of the image in bytes
//$_FILES['image']['tmp_name'] is the temporary filename of the file
//in which the uploaded file was stored on the server
$size=filesize($_FILES['image']['tmp_name']);
//compare the size with the maxim size we defined and print error if bigger
if ($size > MAX_SIZE*1024)
{
echo '<h1>You have exceeded the size limit!</h1>';
$errors=1;
}
//we will give an unique name, for example the time in unix time format
$image_name=time().'.'.$extension;
//the new name will be containing the full path where will be stored (images
//folder)
$newname="upload/".$image_name;
//we verify if the image has been uploaded, and print error instead
$copied = copy($_FILES['image']['tmp_name'], $newname);
if (!$copied)
{
echo '<h3>Copy unsuccessfull!</h3>';
$errors=1;
}}}}
//this code sends the image location to the database
mysql_query("UPDATE blog_users SET image = '$newname' WHERE username = '$session->username'") ;
if( strcasecmp($_SERVER['REQUEST_METHOD'],"POST") === 0) {
$_SESSION['postdata'] = $_POST;
header("Location: ".$_SERVER['PHP_SELF']."?".$_SERVER['QUERY_STRING']);
exit;
}
?>
<form name="newad" method="post" enctype="multipart/form-data"
action=""><img src="" alt="" />
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery-2.0.0.js"></script>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<script type="text/javascript">
$(document).ready(
function(){
$('input:submit').attr('disabled',true);
$('input:file').change(
function(){
if ($(this).val()){
$('input:submit').removeAttr('disabled');
}
else {
$('input:submit').attr('disabled',true);
}
});
});
</script>
</head>
<table>
<tr><td><input type="file" name="image" id="file"></td></tr>
<tr><td><input name="Submit" type="submit" value="Upload image" id="image_upload" disabled>
</td></tr>
</table>
</form>
Comments to answer to close the question.
if(isset($_POST['Submit'])){...} - ... = code to execute.
The way you have it now, is that mysql_query("UPDATE... will run regardless.
In other words, relocate the brace for it.
You have }}}}
Remove one of the braces and relocate it just before your ?> tag, and you should be good to go.
if(isset($_POST['Submit'])){
...
$errors=1;
}}}
//this code sends the image location to the database
mysql_query("UPDATE blog_users SET image = '$newname' WHERE username = '$session->username'") ;
if( strcasecmp($_SERVER['REQUEST_METHOD'],"POST") === 0) {
$_SESSION['postdata'] = $_POST;
header("Location: ".$_SERVER['PHP_SELF']."?".$_SERVER['QUERY_STRING']);
exit;
}
} // brace for if(isset($_POST['Submit']))
?>

PHP: Running Multiple Scripts at the Same Time for the Same Client

I have one PHP script that can take several minutes to complete. The script downloads a file to the user PC.
I have another PHP script and its role is to monitor progress of the main download script. That script is called by the client via AJAX calls and should return download progress information.
Right now, my tests show, that during the execution of the main script(in other words, during file download), the AJAX - monitor script returns no values at all. It starts behaving normally, when the main - Download script finishes.
Is it possible that PHP can not run two or more scripts simultaneously and it allows to run script only in sequential order?
I could insert my code, but I think for the purpose of my question, it is not needed. I simply need to know, if two or more PHP scripts may run simultaneously for the same client.
I use:
WAMP
PHP Version 5.4.12
JavaScript without jQuery
Code Used:
As I was asked to show you my code, please, see the below code parts.
Main PHP(later Download) Script:
<?php
// disable script expiry
set_time_limit(0);
// start session if session is not already started
if (session_status() !== PHP_SESSION_ACTIVE)
{
session_start();
}
// prepare session variable
$_SESSION['DownloadProgress'] = 0;
for( $count = 0; $count < 60; $count++)
{
sleep(1);
echo "Iteration No: " . $count;
$_SESSION['DownloadProgress']++;
echo '$_SESSION[\'DownloadProgress\'] = ' . $_SESSION['DownloadProgress'];
flush();
ob_flush();
}
?>
Monitoring PHP script:
// construct JSON
$array = array("result" => 1, "download_progress" => $_SESSION['DownloadProgress']);
echo json_encode($array);
?>
JavaScript code, where I call the both PHP scripts:
SearchResults.myDownloadFunction = function()
{
console.log( "Calling: PHP/fileDownload.php" );
window.location.href = 'PHP/fileDownload.php?upload_id=1';
console.log( "Calling: getUploadStatus()" );
FileResort.SearchResults.getUploadStatus();
console.log( "Called both functions" );
};
JavaScript AJAX:
// call AJAX function to get upload status from the server
SearchResults.getUploadStatus = function ()
{
var SearchResultsXMLHttpRequest = FileResort.Utils.createRequest();
if (SearchResultsXMLHttpRequest == null)
{
console.log("unable to create request object.");
}
else
{
SearchResultsXMLHttpRequest.onreadystatechange = function ()
{
console.log("Response Text: " + SearchResultsXMLHttpRequest.responseText);
console.log("AJAX Call Returned");
if ((SearchResultsXMLHttpRequest.readyState == 4) && (SearchResultsXMLHttpRequest.status == 200))
{
//if (that.responseJSON.result == "true")
{
var responseJSON = eval('(' + SearchResultsXMLHttpRequest.responseText + ')');
console.log("Download Progress: " + responseJSON.download_progress);
}
}
}
var url = "PHP/fileDownloadStatus.php";
SearchResultsXMLHttpRequest.open("POST", url, true);
SearchResultsXMLHttpRequest.send();
}
};
Code Update I:
PHP Script that will later download files:
<?php
// disable script expiry
set_time_limit(0);
for( $count = 0; $count < 60; $count++)
{
sleep(1);
}
?>
PHP Monitoring script that outputs test values:
<?php
$test_value = 25;
// construct JSON
$array = array("result" => 1, "download_progress" => $test_value);
//session_write_close();
echo json_encode($array);
?>
Both scripts are called followingly:
SearchResults.myDownloadFunction = function()
{
console.log( "Calling: PHP/fileDownload.php" );
window.setTimeout(FileResort.SearchResults.fileDownload(), 3000);
console.log( "Calling: getUploadStatus()" );
window.setInterval(function(){FileResort.SearchResults.getDownloadStatus()}, 1000);
console.log( "Called both functions" );
};
Without more info there are a few possibilities here, but I suspect that the issue is your session. When a script that uses the session file start, PHP will lock the session file until session_write_close() is called or the script completes. While the session is locked any other files that access the session will be unable to do anything until the first script is done and writes/closes the session file (so the ajax calls have to wait until the session file is released). Try writing the session as soon as you've done validation, etc on the first script and subsequent scripts should be able to start.
Here's a quick and dirty approach:
The "Landing" page:
This is the page that the user is going to click the download link
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>
$(document).ready(function(e) {
//Every 500ms check monitoring script to see what the progress is
$('#large_file_link').click(function(){
window.p_progress_checker = setInterval( function(){
$.get( "monitor.php", function( data ) {
$( ".download_status" ).html( data +'% complete' );
//we it's done or aborted we stop the interval
if (parseInt(data) >= 100 || data=='ABORTED'){
clearInterval(window.p_progress_checker);
}
//if it's aborted we display that
if (data=='ABORTED'){
$( ".download_status" ).html( data );
$( ".download_status" ).css('color','red').css('font-weight','bold');
}
})
}, 500);
});
});
</script>
</head>
<body>
<div class="download_status"><!-- GETS POPULATED BY AJAX CALL --></div>
<p>Start downloading large file</p>
</body>
</html>
The "File Uploader"
This is the PHP script that serves the large file... it breaks it into chunks and after sending each chunk it closes the session so the session becomes available to other scripts. Also notice that I've added a ignore_user_abort/connection_aborted handler so that it can take a special action should the connection be terminated. This is the section that actually deals with the session_write_close() issue, so focus on this script.
<?php
/*Ignore user abort so we can catch it with connection_aborted*/
ignore_user_abort(true);
function send_file_to_user($filename) {
//Set the appropriate headers:
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($filename));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($filename));
$chunksize = 10*(1024); // how many bytes per chunk (i.e. 10K per chunk)
$buffer = '';
$already_transferred =0;
$file_size = filesize( $filename );
$handle = fopen($filename, 'rb');
if ($handle === false) {
return false;
}
while (!feof($handle)) {
/*if we're using a session variable to commnicate just open the session
when sending a chunk and then close the session again so that other
scripts which have request the session are able to access it*/
session_start();
//see if the user has aborted the connection, if so, set the status
if (connection_aborted()) {
$_SESSION['file_progress'] = "ABORTED";
return;
}
//otherwise send the next packet...
$buffer = fread($handle, $chunksize);
echo $buffer;
ob_flush();
flush();
//now update the session variable with our progress
$already_transferred += strlen($buffer);
$percent_complete = round( ($already_transferred / $file_size) * 100);
$_SESSION['file_progress'] = $percent_complete;
/*now close the session again so any scripts which need the session
can use it before the next chunk is sent*/
session_write_close();
}
$status = fclose($handle);
return $status;
}
send_file_to_user( 'large_example_file.pdf');
?>
The "File Monitor"
This is a script that is called via Ajax and is in charge of reporting progress back to the Landing Page.
<?
session_start();
echo $_SESSION['file_progress'];
?>

Categories