【发布时间】:2012-02-14 15:59:04
【问题描述】:
我在 MySQL 和 Apache 服务器上使用 PDO 和 PHP 已经有一段时间了。我最近的任务是将企业的旧 Web 应用程序转换为新设置。旧设置是标准 Linux Web 堆栈(Apache/PHP/MySQL/Filezilla),新设置将是带有 IIS/PHP(快速 cgi 安装)/SQL Server 2003/无 FTP 的 Windows 服务器 2003。
除了转换 MySQL 语句以更新带有文件访问信息的表之外,我几乎可以进行所有工作。使用带有 SQLSRV 驱动程序的 PDO 并在“文件下载”PHP 脚本中执行插入语句,将多条记录插入到 SQL 表中。
download.php 确实会向 SQL 服务器发出多个查询。一次检查一个表中的文件存在和变量,然后用访问信息更新另一个表。
请参阅下面的 download.php 代码
调试显示 $count 的打印/回显为 1。然而,检查 SQL 服务器记录,总是显示多于一个插入。有时它只是一个额外的行,总共两个,但有时它高达四个被插入的 EXTRA 语句。 $count 在任何情况下都显示为 1。
此特定 PHP 脚本在调用此插入语句之前根据 SQL 数据库验证信息。首先,验证文件访问的身份验证(成功),验证文件存在(成功),然后用下载信息更新访问表(错误),最后将 PDF 提供给用户(成功)。
当我手动向查询分析器发出 INSERT 语句时,它成功并按预期工作;它每次插入一行。该错误似乎与 execute() 的 SQLSRV 或 PDO 实现有关。
我已经在 stackoverflow、serverfault 和全能的 Google 上搜索过有关这方面的信息。用户需要在一个语句/执行中执行多个查询/插入时返回的唯一类型的结果。我的问题是相反的;我希望只执行一个插入语句,但是总是执行多个。
问题是:为什么会发生这种情况以及如何防止多次插入发生?
根据请求更新
访问此文件的代码是来自另一个网页的单个链接。该页面列出了允许用户访问的当前文件,并提供了指向 download.php 脚本的链接,用于 PDF 的验证、更新和实际服务。
查看页面有一个链接列表(在 for 循环中打印),排列如下:
<a href='download.php?f={$item['name']}&t={$type}' target='_blank'>{$item['name']}</a>
当用户点击该链接时,除了上面的 download.php 代码之外,下面的脚本会运行。它成功地提供了 PDF 文件。内容由 download.php 作为 PHP 标头/内联 PDF 发送:
代码见下文
查看服务器日志显示 两个 GET 请求到 download.php 文件:
2012-02-14 17:44:37 W3SVC1785071458 172.17.31.254 GET /download.php f=06304844-1A.pdf&t=av 4090 - 172.17.31.112 Mozilla/5.0+(Windows+NT+6.1)+AppleWebKit/535.7+(KHTML,+like+Gecko)+Chrome/16.0.912.77+Safari/535.7 200 0 0
2012-02-14 17:44:37 W3SVC1785071458 172.17.31.254 GET /download.php f=06304844-1A.pdf&t=av 4090 - 172.17.31.112 Mozilla/5.0+(Windows+NT+6.1)+AppleWebKit/535.7+(KHTML,+like+Gecko)+Chrome/16.0.912.77+Safari/535.7 200 0 0
我在 Firefox、Opera 和 IE (6-9b) 中测试过,结果是一样的。
更新两次
将整个 download.php 文件放在这里:
<?php
session_start();
require("cgi-bin/auth.php");
// Don't timeout when downloading large files
@ignore_user_abort();
@set_time_limit(0);
//error_reporting(E_ALL);
//ini_set('display_errors',1);
function getfile() {
require('cgi-bin/connect_db_pdf.php');
//Verify information
if (!isset($_GET) || !isset($_GET['f']) || !isset($_GET['t'])) {
echo "Nothing to do!";
exit(0);
}
//Update variables
$vuname = strtolower(trim($_SESSION['uname']));
$file = trim($_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)) {
echo "Non conforming values";
exit(0);
}
try {
$sQuery = "SELECT * FROM pdf_info WHERE PDF_name=:file AND PDF_type=:type";
$statm = $conn->prepare($sQuery);
$statm->execute(array(':file'=>$file,':type'=>$type));
$result = $statm->fetch();
$count = $statm->rowCount();
$sQuery = null;
$statm = null;
if ($count == 1 ){ //File was found in the database so let them download it. Update the time as well
$sQuery = "INSERT INTO access (PDF_name,PDF_type, PDF_time, PDF_access) VALUES (:file, :type, GetDate(), :vuname)";
$statm = $conn->prepare($sQuery);
$statm->execute(array( ':vuname'=>$vuname, ':file'=>$file, ':type'=>$type));
$count = $statm->rowCount();
$sQuery = null;
$statm = null;
$sQuery = "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( ':vuname'=>$vuname, ':file'=>$file, ':type'=>$type));
$sQuery = null;
$statm = null;
//$result is from FIRST SELECT query outside this 'if' scope.
$file_loc = $result['floc'];
$file_name = $result['filename'];
$fileh = fopen($file_loc,'rb');//Send content to browser as inline PDF
header("Content-Type: application/pdf");
header("Pragma: no-cache");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Content-Length: " . filesize($file_loc));
header("Accept-Ranges: bytes");
header("Content-Disposition: inline; filename={$file_name}");
while (!feof($fileh)) {
echo(@fgets($fileh, 8192));
}
fclose ($fileh);
exit(0);
} else { //We did not find a file in the database. Redirect the user to the view page.
header("Location: view.php");
}
} catch(PDOException $err) {//PDO SQL error.
//echo $err;
header('Location: error.php');
exit(0);
}
}
getfile();
?>
【问题讨论】:
-
请发布更多代码。如果你得到多个插入,很可能你的脚本被多次调用,要么是因为错误的逻辑(例如,当帖子尚未提交时),要么是因为有错误的重写规则。
-
在这个脚本运行时观察你的服务器的访问日志,看看你是否真的点击了几次。
-
如果你只做一个简单的测试文件,其中只有与 INSERT 相关的代码,会发生什么?
-
@Michael 我在重新启动 IIS 后检查了服务器日志。该脚本实际上调用了 download.php 两次。如果
$_GET变量未设置或不是预期值,则脚本退出。然后逻辑进入文件信息的检索和下载。为什么这会导致下载脚本或我的函数多次执行? -
@PenguinCoder 请贴出插入代码之前出现的代码,检查
$_GET并退出的部分。
标签: php sql-server pdo