PHP 不会承认自己上传的临时文件
我真的很头疼。
这是我的系统配置:
- Windows Server 2008 R2
- PHP 5.3.8 安装为 FastCGI 模块
- IIS 7.5
这是我的问题:
我有一个简单的文件上传表单。我们知道,当 PHP 接受文件上传时,文件会被赋予一个临时名称,并在处理之前放置在临时目录中。就我而言,PHP 将文件放置在临时目录中(恰好是 E:\Inetpub_IIS\tmp,紧邻 E:\Inetpub_IIS\wwwroot),但随后立即“忘记”该文件存在,直到垃圾收集器出现,这会删除临时文件。更具体地说,临时文件是在服务器上的临时目录中创建的,但是当我对该文件调用 sha1_file() 时,该函数不会返回任何内容。 file_exists() 也失败。这让我认为 PHP 找不到该文件。下面的 ProcMon 日志显示 PHP 正在寻找正确的位置。
这是我的 ProcMon 日志:
2:43:14.9175650 PM php-cgi.exe 5020 CreateFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Desired Access: Generic Read, Disposition: Create, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: N, ShareMode: None, AllocationSize: 0, Impersonating: NT AUTHORITY\IUSR, OpenResult: Created
2:43:14.9182596 PM php-cgi.exe 5020 CloseFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS
2:43:14.9184424 PM php-cgi.exe 5020 QueryOpen E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS CreationTime: 12/27/2011 2:43:14 PM, LastAccessTime: 12/27/2011 2:43:14 PM, LastWriteTime: 12/27/2011 2:43:14 PM, ChangeTime: 12/27/2011 2:43:14 PM, AllocationSize: 0, EndOfFile: 0, FileAttributes: A
2:43:14.9185907 PM php-cgi.exe 5020 CreateFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Desired Access: Write Attributes, Synchronize, Disposition: Open, Options: Synchronous IO Non-Alert, Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, Impersonating: NT AUTHORITY\IUSR, OpenResult: Opened
2:43:14.9187896 PM php-cgi.exe 5020 SetBasicInformationFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS CreationTime: 0, LastAccessTime: 0, LastWriteTime: 0, ChangeTime: 0, FileAttributes: AN
2:43:14.9188368 PM php-cgi.exe 5020 CloseFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS
2:43:14.9190234 PM php-cgi.exe 5020 CreateFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Desired Access: Generic Read/Write, Disposition: OverwriteIf, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: N, ShareMode: Read, Write, AllocationSize: 0, Impersonating: NT AUTHORITY\IUSR, OpenResult: Overwritten
2:43:14.9193771 PM php-cgi.exe 5020 WriteFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Offset: 0, Length: 5,119, Priority: Normal
2:43:14.9489663 PM php-cgi.exe 5020 WriteFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Offset: 5,119, Length: 5,119, Priority: Normal
2:43:14.9730524 PM php-cgi.exe 5020 WriteFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Offset: 10,238, Length: 5,119
2:43:15.0054693 PM php-cgi.exe 5020 WriteFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Offset: 15,357, Length: 5,119, Priority: Normal
2:43:15.0309328 PM php-cgi.exe 5020 WriteFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Offset: 20,476, Length: 5,119
2:43:15.0633978 PM php-cgi.exe 5020 WriteFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Offset: 25,595, Length: 5,119
2:43:15.0879028 PM php-cgi.exe 5020 WriteFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Offset: 30,714, Length: 5,119, Priority: Normal
...
2:43:17.1849721 PM php-cgi.exe 5020 WriteFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Offset: 383,925, Length: 5,119
2:43:17.1851664 PM php-cgi.exe 5020 WriteFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Offset: 389,044, Length: 2,343
2:43:17.1852283 PM php-cgi.exe 5020 CloseFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS
2:43:17.5070914 PM php-cgi.exe 5020 QueryDirectory E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Filter: php3F86.tmp, 1: php3F86.tmp
2:43:17.5083973 PM php-cgi.exe 5020 QueryDirectory E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Filter: php3F86.tmp, 1: php3F86.tmp
2:43:17.5112593 PM php-cgi.exe 5020 QueryDirectory E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Filter: php3F86.tmp, 1: php3F86.tmp
2:43:17.5120519 PM php-cgi.exe 5020 QueryDirectory E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Filter: php3F86.tmp, 1: php3F86.tmp
2:43:27.5512956 PM php-cgi.exe 5020 CreateFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Desired Access: Read Attributes, Delete, Disposition: Open, Options: Non-Directory File, Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
2:43:27.5515084 PM php-cgi.exe 5020 QueryAttributeTagFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Attributes: A, ReparseTag: 0x0
2:43:27.5515406 PM php-cgi.exe 5020 SetDispositionInformationFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Delete: True
2:43:27.5515879 PM php-cgi.exe 5020 CloseFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS
如您所见,ProcMon 清楚地显示了正在创建、写入然后关闭的临时文件。接近尾声时,您可以看到“QueryDirectory”调用,它与我的脚本调用一致,它尝试获取文件的 SHA1 哈希值以及其他内容。
这是我的脚本:
文件上传表单有一个 Flash 对象和一些用于 Flash 对象构建表单的 DIV,仅此而已。临时上传文件是在服务器上完整创建的,所以我非常怀疑我的表单有问题。
<?php
// *******************************************************************
// exhibit-upload.php
//
// *******************************************************************
// Reset same session ID because Adobe Flash is a flaming pile
session_id($_POST['sessionid']);
ob_start("ob_gzhandler");
require_once('inc-common.php');
$logFile = "logfile.txt";
$logHandle = fopen($logFile, 'w');
fwrite($logHandle, '$_FILES error: ' . $_FILES['error'] . "\n");
if(!empty($_FILES))
{
// Get temp file
$sFileTemp = $_FILES['Filedata']['tmp_name'];
$sFileName = $objMySQL->sanitize($_FILES['Filedata']['name']);
fwrite($logHandle, "Permanent Filename: " . $sFileName . "\n");
$aFileBits = explode('.', $_FILES['Filedata']['name']);
$sFileExt = $aFileBits[count($aFileBits) - 1];
// Get SHA1 hash
$sFileHash = sha1_file($sFileTemp);
fwrite($logHandle, "Temp File Exists: " . file_exists($sFileTemp) . "\n");
fwrite($logHandle, "Temp File Name: " . $sFileTemp . "\n");
fwrite($logHandle, "File Hash: " . $sFileHash . "\n");
sleep(10);
exit();
}
?>
“logfile.txt”的内容:
$_FILES error:
Permanent Filename: picture.jpg
Temp File Exists:
Temp File Name: E:\Inetpub_IIS\tmp\php3F86.tmp
File Hash:
“sleep”调用的存在是为了让我有时间在文件消失之前检查文件的临时目录。
数十次谷歌搜索让我找到了有关权限的问题,或者找到了涉及损坏的上传表单、根本无法上传任何内容的解决方案。这些文件是在服务器上创建的,因此显然该表单可以工作。另外,我尝试授予 IUSR、IIS_ISURS 和 DefaultAppPool 对临时目录和所有 E:\Inetpub_IIS 的完全访问权限,以查看这是否与相关权限有关,但这并没有改变任何内容。谁能就这里发生的事情提供一些建议?
编辑:我想通了。
DaveRandom 和我都认为这是某种类型的权限问题,这是事实。然而,当问题实际上是 PHP 权限/配置问题时,我们都在考虑 Windows 权限。戴夫的“向后工作”措辞让我考虑向后移动目录树并测试权限,最终产生了以下解决方案。
我做了什么:
我写了一个非常短的脚本:
<?php
//phpinfo();
echo "Readable: " . is_readable('E:\Inetpub_IIS\tmp');
?>
这返回了 FALSE。显然,正如戴夫所建议的那样,该目录不可读。
我尝试了E:\Inetpub_IIS\wwwroot目录,结果返回TRUE。唔。然后我意识到我一整天都忽略了检查 php_error.log。这是我发现的:
[27-Dec-2011 16:51:43] PHP Warning: is_readable(): open_basedir restriction in effect. File(E:\Inetpub_IIS\tmp) is not within the allowed path(s): (E:\Inetpub_IIS\wwwroot) in E:\Inetpub_IIS\wwwroot\ipl\info.php on line 3
我在 Google 上搜索“open_basedir 限制有效”并得到了答案。在 php.ini 文件中,open_basedir 设置为:
open_basedir = E:\Inetpub_IIS\wwwroot
我将其更改为:
open_basedir = "E:\Inetpub_IIS\wwwroot;E:\Inetpub_IIS\tmp"
重新启动服务器后,应用程序开始按预期工作。
希望这对于其他可能遇到同样问题的人来说是足够的文档。
这个故事的寓意:
- 检查你的 open_basedir 设置。
- 启用、设置并记得检查你的 php 错误日志。
- 不要连续7个小时不间断地盯着同一个问题。我想我差点中风了。
I've got a real head-scratcher here.
This is my system configuration:
- Windows Server 2008 R2
- PHP 5.3.8 installed as a FastCGI module
- IIS 7.5
This is my problem:
I have a simple file upload form. As we know, when PHP accepts a file upload, the file is given a temporary name and placed in a temporary directory before it's processed. In my case, PHP places the file in the temporary directory (which happens to be E:\Inetpub_IIS\tmp, next to E:\Inetpub_IIS\wwwroot) but then immediately "forgets" that the file exists until the garbage collector shows up, which deletes the temporary file. More specifically, the temporary file is created in the temporary directory on the server, but when I call sha1_file() on that file, the function returns nothing. file_exists() also fails. This makes me think that PHP can't find the file. The ProcMon log below shows that PHP is looking in the right spot.
Here is my ProcMon log:
2:43:14.9175650 PM php-cgi.exe 5020 CreateFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Desired Access: Generic Read, Disposition: Create, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: N, ShareMode: None, AllocationSize: 0, Impersonating: NT AUTHORITY\IUSR, OpenResult: Created
2:43:14.9182596 PM php-cgi.exe 5020 CloseFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS
2:43:14.9184424 PM php-cgi.exe 5020 QueryOpen E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS CreationTime: 12/27/2011 2:43:14 PM, LastAccessTime: 12/27/2011 2:43:14 PM, LastWriteTime: 12/27/2011 2:43:14 PM, ChangeTime: 12/27/2011 2:43:14 PM, AllocationSize: 0, EndOfFile: 0, FileAttributes: A
2:43:14.9185907 PM php-cgi.exe 5020 CreateFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Desired Access: Write Attributes, Synchronize, Disposition: Open, Options: Synchronous IO Non-Alert, Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, Impersonating: NT AUTHORITY\IUSR, OpenResult: Opened
2:43:14.9187896 PM php-cgi.exe 5020 SetBasicInformationFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS CreationTime: 0, LastAccessTime: 0, LastWriteTime: 0, ChangeTime: 0, FileAttributes: AN
2:43:14.9188368 PM php-cgi.exe 5020 CloseFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS
2:43:14.9190234 PM php-cgi.exe 5020 CreateFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Desired Access: Generic Read/Write, Disposition: OverwriteIf, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: N, ShareMode: Read, Write, AllocationSize: 0, Impersonating: NT AUTHORITY\IUSR, OpenResult: Overwritten
2:43:14.9193771 PM php-cgi.exe 5020 WriteFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Offset: 0, Length: 5,119, Priority: Normal
2:43:14.9489663 PM php-cgi.exe 5020 WriteFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Offset: 5,119, Length: 5,119, Priority: Normal
2:43:14.9730524 PM php-cgi.exe 5020 WriteFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Offset: 10,238, Length: 5,119
2:43:15.0054693 PM php-cgi.exe 5020 WriteFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Offset: 15,357, Length: 5,119, Priority: Normal
2:43:15.0309328 PM php-cgi.exe 5020 WriteFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Offset: 20,476, Length: 5,119
2:43:15.0633978 PM php-cgi.exe 5020 WriteFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Offset: 25,595, Length: 5,119
2:43:15.0879028 PM php-cgi.exe 5020 WriteFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Offset: 30,714, Length: 5,119, Priority: Normal
...
2:43:17.1849721 PM php-cgi.exe 5020 WriteFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Offset: 383,925, Length: 5,119
2:43:17.1851664 PM php-cgi.exe 5020 WriteFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Offset: 389,044, Length: 2,343
2:43:17.1852283 PM php-cgi.exe 5020 CloseFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS
2:43:17.5070914 PM php-cgi.exe 5020 QueryDirectory E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Filter: php3F86.tmp, 1: php3F86.tmp
2:43:17.5083973 PM php-cgi.exe 5020 QueryDirectory E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Filter: php3F86.tmp, 1: php3F86.tmp
2:43:17.5112593 PM php-cgi.exe 5020 QueryDirectory E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Filter: php3F86.tmp, 1: php3F86.tmp
2:43:17.5120519 PM php-cgi.exe 5020 QueryDirectory E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Filter: php3F86.tmp, 1: php3F86.tmp
2:43:27.5512956 PM php-cgi.exe 5020 CreateFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Desired Access: Read Attributes, Delete, Disposition: Open, Options: Non-Directory File, Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
2:43:27.5515084 PM php-cgi.exe 5020 QueryAttributeTagFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Attributes: A, ReparseTag: 0x0
2:43:27.5515406 PM php-cgi.exe 5020 SetDispositionInformationFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS Delete: True
2:43:27.5515879 PM php-cgi.exe 5020 CloseFile E:\Inetpub_IIS\tmp\php3F86.tmp SUCCESS
As you can see, ProcMon clearly shows the temporary file being created, written and then closed. Near the end you can see the "QueryDirectory" calls, which coincide with my script calls, which attempt to get the SHA1 hash of the file amongst other things.
This is my script:
The file upload form has a Flash object and some DIVs for the Flash object to build the form, nothing more. The temporary upload file is being created on the server in its entirety so I doubt very seriously that my form is the problem.
<?php
// *******************************************************************
// exhibit-upload.php
//
// *******************************************************************
// Reset same session ID because Adobe Flash is a flaming pile
session_id($_POST['sessionid']);
ob_start("ob_gzhandler");
require_once('inc-common.php');
$logFile = "logfile.txt";
$logHandle = fopen($logFile, 'w');
fwrite($logHandle, '$_FILES error: ' . $_FILES['error'] . "\n");
if(!empty($_FILES))
{
// Get temp file
$sFileTemp = $_FILES['Filedata']['tmp_name'];
$sFileName = $objMySQL->sanitize($_FILES['Filedata']['name']);
fwrite($logHandle, "Permanent Filename: " . $sFileName . "\n");
$aFileBits = explode('.', $_FILES['Filedata']['name']);
$sFileExt = $aFileBits[count($aFileBits) - 1];
// Get SHA1 hash
$sFileHash = sha1_file($sFileTemp);
fwrite($logHandle, "Temp File Exists: " . file_exists($sFileTemp) . "\n");
fwrite($logHandle, "Temp File Name: " . $sFileTemp . "\n");
fwrite($logHandle, "File Hash: " . $sFileHash . "\n");
sleep(10);
exit();
}
?>
The contents of "logfile.txt":
$_FILES error:
Permanent Filename: picture.jpg
Temp File Exists:
Temp File Name: E:\Inetpub_IIS\tmp\php3F86.tmp
File Hash:
The "sleep" call exists to give me time to check the temporary directory for the file before it disappears.
Dozens of Google searches have led me to things concerning permissions, or to solutions that involve broken upload forms that fail to upload anything at all. The files are being created on the server so obviously the form works. Also, I have tried giving IUSR, IIS_ISURS and DefaultAppPool full access to the temporary directory and all of E:\Inetpub_IIS to see if this had something to do with the associated permissions but that did not change anything. Can anyone offer some advice on what is going on here?
EDIT: I figured it out.
DaveRandom and I both thought that it was a permissions problem of some type, which was true. However we were both thinking of Windows permissions, when the problem was actually a PHP permission / configuration issue. Dave's "work backwards" phrasing got me thinking of moving backwards through the directory tree and testing permissions which eventually produced the following solution.
What I did:
I wrote a very short script:
<?php
//phpinfo();
echo "Readable: " . is_readable('E:\Inetpub_IIS\tmp');
?>
This returned FALSE. Apparently the directory wasn't readable, as Dave suggested.
I tried the E:\Inetpub_IIS\wwwroot directory, which returned TRUE. Hmm. I then realized I'd neglected to check the php_error.log all day. Here is what I found:
[27-Dec-2011 16:51:43] PHP Warning: is_readable(): open_basedir restriction in effect. File(E:\Inetpub_IIS\tmp) is not within the allowed path(s): (E:\Inetpub_IIS\wwwroot) in E:\Inetpub_IIS\wwwroot\ipl\info.php on line 3
I Googled "open_basedir restriction in effect" and had my answer. In the php.ini file, open_basedir was set to:
open_basedir = E:\Inetpub_IIS\wwwroot
I changed this to:
open_basedir = "E:\Inetpub_IIS\wwwroot;E:\Inetpub_IIS\tmp"
After restarting the server the app began working as intended.
Hopefully that is enough documentation for someone else who might be having the same problem.
Moral(s) of the story:
- Check your open_basedir setting.
- Enable, set and remember to check your php error log.
- Don't stare at the same problem for 7 hours straight without a break. I think I almost had a stroke.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
正如所承诺的,这是我的答案。这是从 OP 编辑中复制/粘贴的。
我想通了。
DaveRandom 和我都认为这是某种类型的权限问题,这是事实。然而,当问题实际上是 PHP 权限/配置问题时,我们都在考虑 Windows 权限。戴夫的“向后工作”措辞让我考虑向后移动目录树并测试权限,最终产生了以下解决方案。
我做了什么:
我写了一个非常短的脚本:
这返回了 FALSE。显然,正如戴夫所建议的那样,该目录不可读。
我尝试了E:\Inetpub_IIS\wwwroot目录,结果返回TRUE。唔。然后我意识到我一整天都忽略了检查 php_error.log。这是我发现的:
我在 Google 上搜索“open_basedir 限制有效”并得到了答案。在 php.ini 文件中,open_basedir 设置为:
我将其更改为:
重新启动服务器后,应用程序开始按预期工作。
希望这对于其他可能遇到同样问题的人来说是足够的文档。
这个故事的寓意:
As promised, here is my answer. This was copy/pasted from the OP edit.
I figured it out.
DaveRandom and I both thought that it was a permissions problem of some type, which was true. However we were both thinking of Windows permissions, when the problem was actually a PHP permission / configuration issue. Dave's "work backwards" phrasing got me thinking of moving backwards through the directory tree and testing permissions which eventually produced the following solution.
What I did:
I wrote a very short script:
This returned FALSE. Apparently the directory wasn't readable, as Dave suggested.
I tried the E:\Inetpub_IIS\wwwroot directory, which returned TRUE. Hmm. I then realized I'd neglected to check the php_error.log all day. Here is what I found:
I Googled "open_basedir restriction in effect" and had my answer. In the php.ini file, open_basedir was set to:
I changed this to:
After restarting the server the app began working as intended.
Hopefully that is enough documentation for someone else who might be having the same problem.
Moral(s) of the story: