为啥 PHP 认为它正在写入文件,但事实并非如此?
Posted
技术标签:
【中文标题】为啥 PHP 认为它正在写入文件,但事实并非如此?【英文标题】:Why does PHP think it's writing to a file but it isn't?为什么 PHP 认为它正在写入文件,但事实并非如此? 【发布时间】:2020-07-10 08:19:33 【问题描述】:我有一个带有 php pgm 的 html 表单,用于将结果写入文件。我基本上是从网上复制的。这不是火箭手术。它在具有最新 Raspbian 的 Raspberry Pi 上运行 PHP 7。 PHP pgm 报告该文件是可写的,它成功了,等等,但该文件仍然是空的。我已将权限更改为 777(用于测试),但这并没有帮助。我难住了。这是 HTML:
<!DOCTYPE html>
<html>
<head>
<title>Update</title>
</head>
<body>
<FORM NAME ="form1" METHOD ="POST" action="filewrite.php">
<INPUT TYPE = "TEXT" VALUE ="memory" NAME="memory">
<INPUT TYPE = "TEXT" VALUE ="desc" NAME="desc">
<INPUT TYPE = "TEXT" VALUE ="comm" NAME="comm">
<INPUT TYPE = "TEXT" VALUE ="reset" NAME="reset">
<INPUT TYPE = "Submit" Name = "Submit1" VALUE = "Create">
</FORM>
</body>
</html>
这是 PHP(在 filewrite.php 中):
<?php
ini_set('display_errors', true);
if(isset($_POST['memory']) && isset($_POST['desc']) && isset($_POST['comm']) && isset($_POST['reset']))
# $data = $_POST['memory'] . '\t' . $_POST['desc'] . '\t' . $_POST['comm'] . '\t' . $_POST['reset'] . "\n";
$data = "testing";
$file = "/tmp/data.txt";
var_dump($_POST);
if (is_writable($file))
echo nl2br("\n\nThe file is writable\n\n");
else
echo nl2br("The file is not writable\n\n");
touch($file);
$ret = file_put_contents($file, $data, LOCK_EX);
if($ret === false)
die('There was an error writing this file');
else
echo nl2br("$ret bytes written to file\n");
echo nl2br("data = $data\n");
echo nl2br("file = $file\n");
else
die('no post data to process');
?>
这是我得到的输出:
array(5) ["memory"]=> string(4) "fred" ["desc"]=> string(6) "barney" ["comm"]=> string(5) "wilma" ["reset"]=> string(5) "betty" ["Submit1"]=> string(6) "Create"
The file is writable
7 bytes written to file
data = testing
file = /tmp/data.txt
这是文件:
-rwxrwxrwx 1 pi pi 0 Mar 29 16:43 /tmp/data.txt
我在没有文件存在的情况下尝试了它,并且在那里有它。起初我试图将表单数据写入文件,但后来切换到只是尝试编写“测试”。我已经尝试了该文件的其他目录,但它们(如预期的那样)由于权限而失败。
编辑: 当我尝试将文件写入 /home/pi 时(我预计会失败,因为网络服务器是 www-data),我在 /var/log/apache2/error.log 中收到此错误:
[Sun Mar 29 17:04:24.648150 2020] [php7:warn] [pid 12075] [client fe80::cceb:ba3c:da1d:6b2a:53052] PHP Warning: file_put_contents(/home/pi/data.txt): failed to open stream: Permission denied in /var/www/html/filewrite.php on line 19, referer: http://devberry-pi.local/rwupdate.html
将其设置回 /tmp/data.txt 清除了该错误(并且不会生成其他错误。)
编辑 2: 我将一个 6k 文件复制到 /tmp 以确保有足够的空间并且工作正常。我现在只写 7 个字节(“测试”),并且真的只想写
编辑 3: 全新的卡,全新的安装,同样的结果。 8^( 我认为我的下一步是使用不同的 Raspberry Pi,尽管我不确定硬件可能是什么问题。注意:我可以从命令行在 /tmp 中创建文件。
哦,新开发 -- 我创建了一个新文件夹 /home/www,将其权限设置为 777,它确实成功地在其中创建了文件。 /tmp 的权限是 drwxrwxrwt,它由 root/root 拥有。 webserver/php 用户是 www-data。我可以将其用作解决方法,但在我的书中并不理想。有没有比 /tmp 更好的地方来放置不同用户/pgms 需要访问的东西?
有什么想法吗?我应该看什么?
【问题讨论】:
您好:您的 PHP 错误日志中有什么内容? /var/log/apache2/error.log 中没有任何内容(除了我之前尝试各种疯狂的事情时)。是否还有其他日志文件要检查? /tmp FS 已满?尝试手动将文件写入/tmp/file.txt,cat > /tmp/file.txt
...
我检查了 /tmp 目录 -- 我将一个 6k 文件复制到 /tmp 并且很好...我只是(当前)尝试写入 7 个字节(“测试”)。
检查您的 Pi 的 SD 卡。我有一个表现出类似行为的人,结果发现卡文件系统已损坏。具体来说,似乎所有写入都在工作,但在重新启动后,根本没有写入任何更改。
【参考方案1】:
您可以使用 PHP 函数 file_put_contents
获取您希望写入的文件名和数据并将其写入文件。在您的情况下,这可能是发送的某种形式的 HTML POST 数据。在下面的解决方案中,我将值编码为 JSON,因为它很好地表示了与此类表单相关联的键/值对。这可以以适合您需要的任何其他形式完成。
我已选择将文件写入与包含表单的 PHP 文件相同的文件夹。这在生产环境中是不现实的,但它表明 PHP 可以保存文件。
如果文件的位置是问题所在,那么权限就是您应该查看的位置,因为在使用以下方法时,这应该是导致文件无法写入的唯一原因。我已在表单字段中输入值以便于测试,但占位符应保留在生产环境中并删除值。
在任何环境中都应该同时进行客户端和服务器端验证。为了简单起见,我已经删除了验证,并且它是题外话,但每个值都应该在进行任何其他操作之前进行测试和过滤。无论如何,这是合并到一个文件中的工作示例:
<?php
/**
* Check for submission of form.
*/
$nl="<br>";
if (isset($_POST['memory']) && isset($_POST['desc']) && isset($_POST['comm']) && isset($_POST['reset']))
try
$data = json_encode($_POST, JSON_PRETTY_PRINT);
$file = "data.txt";
if ( file_put_contents ($file, $data) )
$msg = "File successfully saved.";
else
$msg = "Error saving file";
catch (Exception $e)
$msg = "Error saving file:" .$nl.$nl . $e->getMessage() . $nl.$nl . $e-getTrace();
?>
<!doctype html>
<html lang="en_US">
<head>
<meta charset='utf-8'>
<title>Example: Save File</title>
</head>
<body>
<?php if (isset($msg)) echo '< style="color:#100">'.$msg.'</p>'; ?>
<form name="form1" method="post" enctype="multipart/form-data">
<input type="text" placeholder="memory" name="memory" value="mem">
<input type="text" placeholder="desc" name="desc" value="something">
<input type="text" placeholder="comm" name="comm" value="more data">
<input type="text" placeholder="reset" name="reset" value="reset">
<input type="Submit" Name = "Submit1" value="Create">
</form>
</body>
</html>
此外,提交表单时,您应该在表单标签上设置 enctype 属性。如果您要上传文件<input type="file">
,则必须将其设置为multipart/form-data
。这允许通过 HTTP Post 二进制发送数据。
<form enctype="multipart/form-data" action="..." id="....">
此外,正如 MDN 在其文档中指出的那样,您可以将属性添加到 <input>
或 button
元素。:
“...或
<input>
或<button>
元素的formenctype
属性。”
在所有情况下,您都应该指定表单的 enctype,包括在使用 XMLHttpRequest
的异步请求期间。
【讨论】:
以上是关于为啥 PHP 认为它正在写入文件,但事实并非如此?的主要内容,如果未能解决你的问题,请参考以下文章