I have a file upload page in my application. I need to show "Uploading" while file is uploading then show "Processing" while file is processing. Then after completion of script my page got redirected to some url.
I have tried to use PHP SESSIONS in the script. As in code below:
$_SESSION['uploaded']=0;
if (!empty($_FILES)) {
$tempFile = $_FILES['file']['tmp_name'];
$targetPath = dirname( __FILE__ ) . $ds. $storeFolder . $ds;
$_FILES['file']['name']=date('Ymdhis').$_FILES['file']['name'];
$targetFile = $targetPath. $_FILES['file']['name'];
if(move_uploaded_file($tempFile,$targetFile)){
$_SESSION['uploaded']=1;
//some processing here which takes some 4-5second to complete
}
}
After file upload complete I update session. I am checking session every second by calling following function in javascript:
function countdown(seconds){
console.log(<?php echo $_SESSION['uploaded']; ?>);
if(<?php echo $_SESSION['uploaded']; ?>==0){
setTimeout(function() {
//uploading
seconds--;
countdown(seconds);
}, 1000);
}
else{
//processing
}
}
After searching from google for long time I came to know that in a single script SESSION is locked till script execution completed. Then I used session_write_close(); But it also not works. I am always getting 0 value of SESSION.
Please help me figuring out solution in simplest way. Thanks.
UPDATE
Unable to make it work with Ajax request also. So further tried using the MySQL table.
What I do is create table when upload script is called. Then insert value of status=0 in it using following code:
$session=session_id();
$stmt=$conn->prepare("DROP TABLE IF EXISTS $session");
$stmt->execute();
$stmt=$conn->prepare("CREATE TABLE $session (id INT(11), status INT(11))");
$stmt->execute();
$stmt=$conn->prepare("INSERT INTO $session VALUES(1,0)");
$stmt->execute();
Then after upload completion I update the status to 1 and do the processing on file.
Then after successful completion of script I redirect to result page and drop table using session_id().
But My Ajax script which is checking status every second doesn't respond till the upload.php script ends. I have tried closing connection after every query but in vain. Code on getstatus.php
<?php
session_start();
$session=session_id();
require_once('connect.php');
$stmt=$conn->prepare("SELECT * FROM $session WHERE id=1");
$stmt->execute();
$res=$stmt->fetch(PDO::FETCH_ASSOC);
echo $res['status'];
?>
Unable to find solution for it till now. Help is greatly appreciated. Thanks.
Instead of invoking a PHP process on the server side every second, you could use a static file to check the upload state.
When generating the upload form for the client:
Create a tempnam for a directory that is accessible for the
client.
Write 'uploading' to the temporary file
Store the filename in the session. (Be aware: The user might open multiple upload forms. Store the filenames in an array)
Send the filename to the client as a hidden field.
On the server side after user submitted the form:
Check if filename sent from the client matches a filename stored in the session.
Write 'processing' to the state file
At the end of your upload script write 'finished' to the state file
On the client side after user submits the form, check the upload state by doing ajax requests on the state file.
Remarks
Disable caching for the state file with .htaccess. If this is no option you can achieve the same behavior with a php state script and the upload state saved to a session variable instead of a state file.
To make sure all generated files are deleted register a destroy handler that deletes files generated in the session: http://php.net/manual/en/function.session-set-save-handler.php
<?php echo $_SESSION['uploaded']; ?> is preprocessed by PHP only once, just before this javascript is sent to client. That said, the javascript on client looks like:
function countdown(seconds){
console.log(0);
if(0==0){
setTimeout(function() {
//uploading
seconds--;
countdown(seconds);
}, 1000);
}
else{
//processing
}
}
You should find other way (ajax?) to update information on the client side.
This became too long for a comment.
I'm unsure how you'd respond with progress information with PHP. I tried once and failed.
Socket.io is awesome in Node.js and there is a PHP server emitter. I would potentially give that a go. It should offer near instantaneous communication without waiting for scripts to complete.
Alternatively I would check out Jquery upload, it has a PHP server script. Supports progress bars Jquery Upload. Either implement it directly or check out the source code for how display progress info. I tried having a quick look but couldn't identify how they do it easily.
Why use DATABASE if you can do it on server ?
To save your bandwidth and database traffic you can seperate your process into 2 file
Create upload.php to serve uploading process
$_SESSION['uploaded']=0;
if (!empty($_FILES)) {
$tempFile = $_FILES['file']['tmp_name'];
$targetPath = dirname( __FILE__ ) . $ds. $storeFolder . $ds;
$_FILES['file']['name']=date('Ymdhis').$_FILES['file']['name'];
$targetFile = $targetPath. $_FILES['file']['name'];
if(move_uploaded_file($tempFile,$targetFile)){
// Save path to session var
$_SESSION['uploaded']=$targetFile;
//You can tell client if the uploading process were done and show 'Processing ...'
// Place some code
exit;
}
}
Next, create a file called progress.php
// check
if(!empty($_SESSION['uploaded'])){
// Do your processing code here
// Remove session
unset($_SESSION['uploaded']);
// Then send response to client after your processing were done
echo 'Done';
exit;
}
You can redirect client using jquery as you tagged it. Good luck
Related
I was wondering if it was possible to send HTTP response immediately and continue the script.
Background: Among all the petition I make to the server there's one that creates an Excel file (using PHPSpreadSheet), since creating this files can take a little longer I was thinking of responding a HTTP 202 status code to the Client, something like:
header("HTTP/1.1 202 Excel file in process");
and program in JavaScript a listener on the main js file for every time that status code arrives to activate an interval (setInterval()) and ask every certain amount of seconds whether the Excel file is ready or not
$(document).ajaxSuccess(function ( event, xhr, settings) {
if(xhr.status === 202) {
//Activate the setInterval function to ask the server every 10 seconds whether the file is ready or not
}
});
(I know I have to be activating/deactivating the interval)
So whenever I receive a petition on my createExcel.php file I would like to respond the user immediately that the petition has being received and start the making of such Excel file(s)
Something like:
<?php
//Tell user that the petition has being received
header("HTTP/1.1 202 Excel file in process");
//Kill this process or close this connection but continue executing
//Call createExcel.php
the createExcel.php file would update some table in the database the confirm the file has been created, same table that the interval petition will be consulting every 10 seconds
That's what I'm attempting to do, I would just like you guys to tell me how to call another another file without waiting for such called file to finish to respond the user.
I was thinking of using exec() but I have never used it (I'm testing it right after I post this), and most importantly any experience or tips would be greatly appreciated (like optimization tips and the like)
I saw this question here on Stack Overflow, but the answer suggests to create a cron service which is not a solution for me.
Thank you!
Edit---
Hey in case someone sees this I found two solutions to my question:
The first one I tried but gave a lot of trouble with permissions is this: https://code-boxx.com/php-background-process/
But this one would work beautifully if you run it from cmd, but when you run it thought the browser, Apache forbids you from using executing commands; so exec(), popen(), and similar commandss won't work unless you change your permissions in your folders, which I consider a security issue, so I found out this very beautiful function fastcgi_finish_request()
Edit 2 - solution
https://qastack.mx/programming/15273570/continue-processing-php-after-sending-http-response
this works we flush all content in buffer and close the connection and then we just continue the execution of the script.
In JS:
$(document).ajaxSuccess(function ( event, xhr, settings) {
if(xhr.status === 202) {
//console.log("Your file is in process");
//console.log(xhr.responseJSON);
//Activate interval to check if file is ready
}
});
//Make petition
$.post("petitionsHandler.php", {"texto":"Hello world, greeting from México!"}, function (resp){
//Handle responses
}, "json").fail(function () {
//Handle errors
});
In PHP
<?php
$text = $_POST["text"];
//If you are using sessions don't forget to close the session file
//Or such will be blocked until long script finishes
session_write_close();
header("HTTP/1.1 202 Solicitud recibida"); #This is the code I wanted to send
header("Content-Type: application/json"); #Depends the kind of data you're sending to the client
// Buffer all upcoming output...
ob_start();
// Send your response.
echo '{"success":true, "message":"Your request has been received."}';
// Get the size of the output.
$size = ob_get_length();
// Disable compression (in case content length is compressed).
//header("Content-Encoding: none"); #I didn't need this but check your situation
// Set the content length of the response.
header("Content-Length: {$size}");
// Close the connection.
header("Connection: close");
// Flush all output.
ob_end_flush();
ob_flush();
flush();
//All outputs have now been sent to the client
//You can continue executing your long tasks
include_once('createExcel.php');
createExcel.php
<?php
sleep(10); #You can use to checkthat the code works
//The above code will respond the client immediately and after ten seconds the Excel file will be created
require "PHPSpreadSheet/vendor/autoload.php";
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->setCellValue('A1', $text);
$writer = new Xlsx($spreadsheet);
$writer->save('MyFile.xlsx');
Of course you have to make validations, so you don't have lots of running process in background but I hope the general idea has been shown in this example.
This is not exactly the code I use, but this is all it takes to continue executing code after responding the client.
In my own opinion, I thinks this is better than using exec() or similar functions which invoke the command terminal as that could be a potential vulnerability , so you don't have to change permissions or anything.
Note: If you're using sessions, Please remember to use session_write_close() because on heavy tasks will block the file until such task is finished.
I hope it helps :)
My answer was based on this blog: https://qastack.mx/programming/15273570/continue-processing-php-after-sending-http-response
I'm sending some files through the dropzone.js to my PHP server. I'm having a problem validating the max files being sent PHP side. Even though I will use the maxFiles param for dropzone.js I would like to make sure nothing odd is being sent more times than needed. The main problem is that dropzone can send the file per POST request, so I cannot simply count($_FILES) because there is always gonna be 1.
So I decided to store the temporary uploads in the database using the session id and the field hash and select the count of uploads by that hash.
Unfortunately, that doesn't work as intended, since another request is being sent too fast.
Code is:
if ($request->ajax())
{
$hash = md5(Session::getId() . '_myupload');
$count = DB::query("SELECT COUNT(id) FROM temp_uploads WHERE hash = ?")->param($hash)->execute();
echo $count; // 0
// Store the file
DB::query("INSERT INTO temp_uploads (key) VALUES (?)")->param($hash)->execute();
$request->file('myupload')->store('uploads');
}
I'm uploading two files and the second request is being sent before the INSERT query even finished executing.
How can I properly validate the max files from multiple request in PHP?
I am trying to save a variable for counting the views of my personal website, I dont need to use php because its literally a viewcount. I know how to retrieve the count from the server using $.post, but how would I retrieve it (Edit: In the simplest way possible.)?
The website I'm trying to do it with is http://artsicleprojects.com/
Thanks in advance!
You will need PHP for this question, because it is dealing with server-side actions. First, you need to make a server-side script to increment the text file's number. Then, you will need to make a client-side script to make a request to the server. This script increments a number in the text file every time the request is made. Anyway, here's how I would do it (Note: this code is un-tested):
PHP:
<?php
/*Reads and collects current count.*/
$rfile = fopen("views.txt", "r") or die("Unable to open file!");
$count = fread($rfile,filesize("views.txt"));
fclose($rfile);
/*Increments the count.*/
$wfile = fopen("views.txt", "w");
$ncount = $count + 1;
fwrite($wfile, $ncount);
fclose($wfile);
?>
Note on code: for this code to work correctly, you may need a text file already made (views.txt), in the same directory as the PHP script, with a single "0" written in it.
JavaScript (with jQuery):
$.post("phpscript.php", function(data, status){
console.log(status);
});
This also is supposed to be in the same directory as the script to work.
I using PHPExcel to generate excel file, but due to some excel file is quite big, it takes time to generate.
When excel file is generating, I wish to add a popup that shows either progress bar or a waiting icon.
I've tried all the solution I found on google but still cannot be done.
I appreciate all kind of help.
$objPHPExcel = new PHPExcel();<br>
$F=$objPHPExcel->getActiveSheet();
//other's code to generate excel file
header('Content-Type: application/vnd.ms-excel');<br>
header('Content-Disposition: attachment;filename="report.xls"');<br>
header('Cache-Control: max-age=0');
$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5');<br>
$objWriter->save('php://output');<br>
exit();
The excel start to generate in this line of code:
$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5');
There is no way to have this work the way you are proposing. First, the headers you are creating will not allow html to show to the user. Also, the php hasn't finished processing so there is no way to go back to the user until the script is done.
Another option would be to call to the PHP script from Javascript asynchronously. Then return the file's name to the javascript, and redirect the user to the Excel file. Then you can show whatever you need to the user on the web page. Below is a very very very simplified example. But it could get you started.
php:
$objPHPExcel = new PHPExcel();
$F=$objPHPExcel->getActiveSheet();
//Other code to Generate Excel File
$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5');
$objWriter->save('path/to/excelfile.xls');
//Now return some info to user
//You will want to actually do some testing here
//to make sure the file was created
echo json_encode(array(
"result" => 200,
"path" => "path/to/excelfile.xls"
));
jquery:
//Call to the php file above to start the processing
//You can add some spinner or popup message here
$.get( "path/to/phpexcel.php", function( data ) {
//Now check the return data
if(data.result === 200){
//If all is well just redirect the user to the file
window.location = data.path;
}else{
alert("Something went wrong!!!");
}
});
Again, this is very rough, but I just wanted you to have an option to pursue further.
Since this is an old post, I'll keep it short.
If you place a iFrame on your page and load your PHPExcel script into it with echoes placed at certain points of informing what is happening, you can simulate this. Other option is to load the PHP_Excel script as its' own page with the above echoes in place and a continue or return button that shows when the script is finished.
Hello fellow coders!
I have a php script that saves the url's passed from a form to a zipped archive, which works just fine.
The only trouble is when a user submits the form after selecting a lot of urls, the user has to wait a while whilst the php script does it's stuff.
I would like to use ajax to have a sort of verbose feedback after each url is added to the zip in it's foreach loop, and also flag up if it encountered a php error (it currently does this if one of the url's was a 404 page.)
I've tried a couple of tests with AJAX but gotten nowhere.
Would this even be possible?
My line of thought is that the foreach loop would run as an external php script via AJAX, and pass it's data back to this script on success... or there may be a better way of doing it.
Any help would be greatly appreciated!
<?php
$dir = "archive/";
$site = 'http://domain.com/';
$urls = array($site);
$zipfile = 'archive-'.date('Ymd_His').'.zip';
$zip = new ZipArchive;
$newzip = $zip->open($zipfile, ZipArchive::CREATE);
if (isset($_POST['urls'])){$urls = $_POST['urls'];}
if ($newzip === TRUE) {
foreach ($urls as $url) {
$file = file_get_contents($url);
$filename = "index.html";
$namer = str_replace($site, '', $url);
if ($namer != "") {$filename = $namer;}
$zip->addFromString($dir.$filename, $file);
}
$source = "assets/";
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST);
foreach ($files as $file) {
$file = str_replace('\\', '/', $file);
if( in_array(substr($file, strrpos($file, '/')+1), array('.', '..')) )
continue;
if (is_dir($file) === true){
$zip->addEmptyDir($dir.str_replace($source . '/', '', $file . '/'));
}
else if (is_file($file) === true){
$zip->addFromString($dir.str_replace($source . '/', '', $file), file_get_contents($file));
}
}
$zip->close();
echo "Generated <a href='$zipfile'>$zipfile</a>";
} else {
echo 'failed';
}
?>
For you to use ajax to receive feedback on the progress of your PHP script you can do the following:
Create a HTML page that calls your initial PHP script for ZIP file processing through an ajax call (or using JQuery).
Modify your initial script to add data to a database events table or append/edit information to a physical file (e.g. json).
Write javascript code to query a secondary PHP script (or file) that will provide the status of the ongoing operation.
In the case you use a database, your secondary script must query the database, and provide all new information to the client about how the zip file operations are going.
So summary:
HTML page containing javascript that calls initial php script
HTML page contains function called periodically through setInterval to query URL that provides status information.
PHP script that starts the process contains calls to add status to database or file.
PHP script that returns status/progress of algorithm while running.