2012-02-14 2 views
4

У меня есть веб-приложение с использованием PHP и PDO с подготовленными операторами SQLSRV для отображения ссылок на файлы для загрузки. Внутренний PHP-скрипт «download.php» проверяет различные элементы перед тем, как загружать PDF-файл пользователю. Затем файл download.php должен обновить несколько таблиц SQL и передать файл PDF пользователю.PHP скрипт загрузки PDF-файлов выполняется дважды

Просьба ознакомиться с моим Previous Question и, если вам нужна дополнительная информация, устранение неполадок завершено.

После устранения неполадок произошла ошибка, о которой я думал (и, следовательно, предыдущий вопрос, заданный мной) была неправильной. Сценарий загрузки выполняется несколько раз для каждой загрузки файла.

Я искал журналы сервера и во время отладки с Firebug я вижу, как скрипт download.php делает несколько запросов GET на сервер. Иногда скрипт завершается только один раз, как и ожидалось. В других случаях скрипт выполняет три-четыре запроса для одного щелчка ссылки для загрузки.

Теперь, когда я более полно понимаю, что происходит с ошибкой, мне нужно немного помочь в ее исправлении. Мне нужно, чтобы сценарий запускался несколько раз и, таким образом, обновлял таблицу SQL с записями, которые находятся в пределах нескольких миллисекунд друг от друга.

просмотра страниц проверяет базу данных SQL для файлов текущего пользователя разрешен доступ, и отображает список ссылок:

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

Поскольку значения необходимы для сценария download.php работать, я не может изменить запрос на $ _POST вместо $ _GET.

То, что я пробовал:

  • Проверка/установка переменной сеанса для состояния «загрузка», перед getfile() который сбрасывает прямо перед exit(0)

  • Положив операторы SQL в отдельном PHP файл и require'ing что

  • Добавление sleep(1) после getfile()

  • Комментирование информации заголовка/PDF

Первые три меры не работают, чтобы предотвратить двойное/тройное выполнение сценария PHP загрузки. Тем не менее, последняя мера предотвращает двойное/тройное выполнение PHP-скрипта, но, конечно, PDF никогда не доставляется в клиентский браузер!

Вопрос: Как я могу гарантировать, что только одна вставка/обновление PER DOWNLOAD ONE вставлена ​​в базу данных или, по крайней мере, как я могу предотвратить выполнение сценария PHP несколько раз?

UPDATE

Скриншот выпуска в поджигатель:

один запрос: one

Две просьбы: two

download.php сценарий

<?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(); 


?> 
+0

Просто уточнить - вы не знаете, что * точно вызывает несколько загрузок, правильно? Я думаю, что стоит продолжать копаться, вместо того, чтобы пытаться исправить этот симптом. (Хотя я понятия не имею, что может вызвать это ...) –

+0

@pekka Правильно; Я не знаю, что вызывает множественные казни, я просто знаю, что они происходят. Кроме того, сам файл не загружается несколько раз, что я могу сказать. Ссылка просмотра только когда-либо открывает одно окно, и один файл загружается за клик. – PenguinCoder

+0

Хм. Если Firebug показывает несколько выстрелов «хитов», для них должна быть причина. Где именно происходят эти хиты? На что они похожи? Вы используете JavaScript на своей странице? Ссылка ссылки на скачивание в другом месте страницы? –

ответ

4

Если вам действительно необходимо убедиться, что ссылка создает событие только один раз, вам необходимо реализовать систему токенов, где при создании гиперссылки (или целевой пост формы) генерируется одноразовый токен и сохраняется (в сеансе или где угодно), а затем проверяется в вызывающем скрипте.

Так что ваша гиперссылка может выглядеть следующим образом:

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

На стороне PHP это действительно упрощенная идея о том, что вы можете сделать:

<?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 

Это позволит решить последствия вашей проблемы , хотя и не двойной. Однако, так как вы хотите убедиться, что ссылка запускает событие только один раз, НЕ ВМЕШАЕТ ЧТО, вы, вероятно, реализуете это в той или иной форме, поскольку это единственный способ гарантировать, что какая-либо ссылка имеет реальную жизнь, о которой я могу думать ,

При генерировании ссылке вы могли бы сделать что-то подобное в вашем коде:

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

я тоже где поставить процедуру очистки, чтобы удалить все использованные маркеры. Кроме того, неплохо было пропустить токены за определенный период, но я думаю, это объясняет это.

+0

Если я правильно вас понимаю, вы заявляете, что создаете индивидуальный токен на 'view.php', которая вставлена ​​в список ссылок для загрузки? Затем в PHP-скрипте проверьте этот токен и продолжайте/сбой при необходимости. Это предотвратит действительность ссылки в другом сеансе, но я не вижу, как это предотвратит двойное выполнение скрипта. (Заданный вопрос основан на том, что пользователь нажимает ссылку для загрузки только один раз). Это сценарий, который выполняется дважды или более. – PenguinCoder

+0

Я угадываю, что что-то увольняет один и тот же запрос несколько раз. Если вы добавили токен к ссылке запроса, тогда даже если ссылка была вызвана несколько раз, каждый раз, когда ссылка была вызвана, тот же токен будет передан. Однако правильное использование токенов, особенно в сочетании с транзакцией базы данных, может помешать выполнению сценария несколько раз. Вы не предотвращаете многократное выполнение сценария. Вы просто препятствуете ему работать до завершения, кроме первого вызова. – mainegreen

+0

Спасибо за ответ, поясняющий ваш ответ. Я тоже думал о том, как предотвратить множественные записи в базе данных, но еще не многократные. Я завернул оператор insert/update в инструкции if, которая проверяет наличие реферера, и, если присутствует, он выполнит SQL. Если нет, и последний запрос был в течение 1 секунды, он пропустит вставку/обновление. Временное исправление до правильной фиксации. Я собираюсь принять ваш ответ, поскольку я попытаюсь реализовать метод токена и БД для проверок по сравнению с тем, что у меня есть в настоящее время. Благодарю. – PenguinCoder

Смежные вопросы