Downloading a PHP PDF script is done twice

I have a web application using PHP and PDO with prepared SQLSRV statements to display file links for users to download. The internal PHP script 'download.php' checks the various elements before uploading a PDF file to the user for download. Then the download.php file should update several SQL tables and serve the PDF file for the user.

Please read my previous question and troubleshooting completed there if you need more information.

After troubleshooting, the error that I was thinking about was (and therefore the previous question I asked) was incorrect. My script download is done several times for each file download.

I searched server logs and while debugging with Firebug, I see that my download.php script makes several GET requests to the server. Sometimes a script completes only once as expected. In other cases, the script executes three to four requests for one click of the download link.

Now that I understand more fully what is happening with the error, I need a little help in fixing it. I need to prevent the script from running several times and thus update the SQL table with records that are within a few milliseconds of each other.

The browse page checks the SQL database for files that the current user is allowed to access and displays a list of links:

<a href='download.php?f={$item['name']}&t={$type}' target='_blank'>{$item['name']}</a> 

Since values ​​are required to download the download.php script, I cannot change the request to $ _POST instead of $ _GET.

What I tried:

  • Check / set the session variable for the load state before getfile() , which is disabled just before exit(0)

  • Putting SQL statements in a separate PHP file and requiring that

  • Adding sleep(1) after getfile()

  • Comment header information / PDF

The first three measures did not help prevent the double / triple loading of the PHP script. However, the last measure prevents double / triple execution of the PHP script, but, of course, PDF is never delivered to the client browser!

Question How can I guarantee that only one PER DOWNLOAD ONE insert / update is inserted into the database, or at least how can I prevent the PHP script from executing multiple times?

UPDATE

Screenshot with a question in firebug:

One request: one

Two queries: two

download.php script

 <?php session_start(); require("cgi-bin/auth.php"); // Don't timeout when downloading large files @ignore_user_abort(1); @set_time_limit(0); //error_reporting(E_ALL); //ini_set('display_errors',1); function getfile() { if (!isset($_GET['f']) || !isset($_GET['t'])) { echo "Nothing to do!"; exit(0); } require('cgi-bin/connect_db_pdf.php'); //Update variables $vuname = strtolower(trim($_SESSION['uname'])); $file = trim(basename($_GET['f'])); //Filename we're looking for $type = trim($_GET['t']);//Filetype if (!preg_match('/^[a-zA-Z0-9_\-\.]{1,60}$/', $file) || !preg_match('/^av|ds|cr|dp$/', $type)) { header('Location: error.php'); exit(0); } try { $sQuery = "SELECT TOP 1 * FROM pdf_info WHERE PDF_name=:sfile AND type=:stype"; $statm = $conn->prepare($sQuery); $statm->execute(array(':sfile'=>$file,':stype'=>$type)); $result = $statm->fetchAll(); $count = count($result); $sQuery = null; $statm = null; if ($count == 1 ){ //File was found in the database so let them download it. Update the time as well $result = $result[0]; $sQuery = "INSERT INTO access (PDF_name,PDF_type,PDF_time,PDF_access) VALUES (:ac_file, :ac_type, GetDate(), :ac_vuname); UPDATE pdf_info SET last_view=GetDate(),viewed_uname=:vuname WHERE PDF_name=:file AND PDF_type=:type"; $statm = $conn->prepare($sQuery); $statm->execute(array( ':ac_vuname'=>$vuname, ':ac_file'=>$file, ':ac_type'=>$type,':vuname'=>$vuname, ':file'=>$file, ':type'=>$type)); $count = $statm->rowCount(); $sQuery = null; $statm = null; //$result is the first element from the SELECT query outside the 'if' scope. $file_loc = $result['floc']; $file_name = $result['PDF_name']; // Commenting from this line to right after the exit(0) updates the database only ONCE, but then the PDF file is never sent to the browser! header("Content-Type: application/pdf"); header("Pragma: no-cache"); header("Cache-Control: no-cache"); header("Content-Length: " . filesize($file_loc)); header("Accept-Ranges: bytes"); header("Content-Disposition: inline; filename={$file_name}"); ob_clean(); flush(); readfile($file_loc); exit(0); } else { //We did not find a file in the database. Redirect the user to the view page. header("Location: view.php"); exit(0); } } catch(PDOException $err) {//PDO SQL error. //echo $err; header('Location: error.php'); exit(0); } } getfile(); ?> 
+4
source share
1 answer

If you really need to make sure that the link only creates the event once, then you need to implement a token system where, when creating a hyperlink (or the goal of the form message), a one-time use token is generated and stored (in the session or anywhere), and then checked in the caller script.

So your hyperlink might look like this:

 <a href='download.php?token={some-token}&f={$item['name']}&t={$type}' target='_blank'>{$item['name']}</a> 

On the php side, this is a really simplistic idea of ​​what you can do:

 <?php session_start(); if (!isset($_REQUEST['token']) die(); // or fail better if (!isset($_SESSION['oneTimeTokens'][$_REQUEST['token']) die(); // or fail better if ($_SESSION['oneTimeTokens'][$_REQUEST['token']=='used') die(); // or fail better $_SESSION['oneTimeTokens'][$_REQUEST['token']='used'; // we're good from this point 

This will solve the consequences of your problem, although not double execution. However, since you want to make sure that the link fires the event only once, DOES NOT INTERVENT, that you are likely to implement this in one form or another, as this is the only way to ensure that any link has real life about which I can think.

When creating the link, you will do something like this in your code:

 <?php $tokenID = {random id generation here}; $_SESSION['oneTimeTokens'][$tokenID] = 'not used'; 

I would place a cleanup procedure somewhere to remove all used tokens. In addition, it would be nice to skip tokens over a certain period, but I think this explains it.

+4
source

Source: https://habr.com/ru/post/1396494/


All Articles