PHP5中while循环内的file_exists()无法正常工作

8

file_exists无法工作。我已经查看了几个示例,但仍然没有效果。程序无法检测到该文件。我的文件路径为/var/www/osbs/PHPAPI/recording.mp3,网站根目录位于osbs内部。该文件的位置在PHPAPI内,因此我没有在file_put_contents中放置完整路径。程序能够制作原始的recording.mp3,但不能制作任何附加版本。

<?php
$actual_name = pathinfo("PHPAPI/recording.mp3",PATHINFO_FILENAME);
$original_name = $actual_name;
$extension = pathinfo("PHPAPI/recording.mp3",PATHINFO_EXTENSION);

if ($_GET["RecordingUrl"]) {
     if (file_exists("/var/www/osbs/PHPAPI/".$actual_name.".".$extension)) {
        $actual_name = find_new_name($original_name, $extension);
     }
     else {
        $actual_name = $original_name;
     }
     $name = $actual_name.".".$extension;
     file_put_contents($name, file_get_contents($_GET["RecordingUrl"]));
}

function find_new_name ( $file, $extension ) {
    $name = $file.".".$extension;
    $i = 0;
    while(file_exists("/var/www/osbs/PHPAPI/".$name)){
        $new_name = $file.$i;
        $name = $new_name.".".$extension;
        $i++;
    }
    return $new_name;
}
 ?>

这是一个巨大的安全问题:file_get_contents($_GET["RecordingUrl"]) - greg0ire
@greg0ire 我知道这只是为了测试。我通常会使用 $_POST。 - fixnode
2
你真有趣。 - greg0ire
直接将任何用户输入传递给file_get_contents()是一种安全风险。 - Enstage
8个回答

2
您的问题与file_put_contents有关。您需要指定完整路径,而不是只指定文件名。在使用之前尝试echo $name,您会发现它不是路径,只是一个文件名。
我建议您在文件开头设置一个常量来指定路径,而不是有时依赖相对路径,有时依赖绝对路径。
<?php
const SAVE_PATH = "/var/www/osbs/";

$actual_name = pathinfo(SAVE_PATH."PHPAPI/recording.mp3",PATHINFO_FILENAME);
$original_name = $actual_name;
$extension = pathinfo(SAVE_PATH."PHPAPI/recording.mp3",PATHINFO_EXTENSION);

if (isset($_GET["RecordingUrl"]) && $_GET["RecordingUrl"]) {
     if (file_exists(SAVE_PATH."PHPAPI/".$actual_name.".".$extension)) {
        $actual_name = find_new_name($original_name, $extension);
     }
     else {
        $actual_name = $original_name;
     }
     $name = $actual_name.".".$extension;

     file_put_contents(SAVE_PATH.'PHPAPI/'.$name, file_get_contents($_GET["RecordingUrl"]));
}

function find_new_name ( $file, $extension ) {
    $name = $file.".".$extension;
    $i = 0;
    while(file_exists(SAVE_PATH."PHPAPI/".$name)){
        $new_name = $file.$i;
        $name = $new_name.".".$extension;
        $i++;
    }
    return $new_name;
}
 ?>

我所做的更改:

  1. 定义了一个 const SAVE_PATH = "/var/www/osbs/";
  2. 在各处使用新的常量。不再有相对路径和绝对路径之分,全部都是绝对路径。
  3. file_put_contents 中使用常量(这是真正的修复方法,请在此处使用完整路径
  4. 添加了额外的检查以确保 RecordingUrl 已设置,否则当其未设置时将收到 PHP 警告。

2

你需要将所有的逻辑都放在find_new_name()函数中。这样可以使你的代码更清晰易懂。

if ($_GET["RecordingUrl"]) {
  $name = find_new_name("PHPAPI/recording.mp3");
  file_put_contents($name, file_get_contents($_GET["RecordingUrl"]));
}

function find_new_name($name) {
  $info = pathinfo($name);
  $name = $info['basename'];
  $i = 0;
  while (file_exists("$info[dirname]/$name")) {
    $name = sprintf('%s%d.%s', $info['filename'], ++$i, $info['extension']);
  }
  return "$info[dirname]/$name";
}

只回答指出这一点,我会说这是主要的罪魁祸首。理智的函数胜利。然而,我必须承认,这可以进一步改进,以便可以重复使用文件名中的现有数字,并且在检测到空闲后创建文件(例如并行上传)的情况下,该函数可以继续查找下一个新名称。 - hakre

2
问题似乎出现在你的脚本第一行:
$actual_name = pathinfo("PHPAPI/recording.mp3", PATHINFO_FILENAME);

这将把recording.mp3分配给$actual_filename。然后,您通过将扩展名连接到文件名来检查是否存在recording.mp3.mp3。我认为您想使用PATHINFO_BASENAME,它将返回没有扩展名的文件名。

这并没有帮助。即使我将路径更改为(file_exists("/PHPAPI/recording.mp3")),file_exists仍然失败。 - fixnode
1
很可能你的包含路径中没有包含包含PHPAPI文件夹的目录。借助get_include_path函数,输出一个包含所有寻找该文件夹的目录列表,如果根目录不在其中,你需要首先添加它或者使用该文件夹的完整路径。 - sjdaws

2

你确定路径没问题吗? /PHPAPI搜索文件应该在/PHPAPI内部,而不是期望的/var/www/osbs/PHPAPI/。你应该检查PHPAPI/$filename


如果您通过SSH登录服务器,/var/www/osbs/PHPAPI是完整路径,但是此域的Apache文档根目录是osbs文件夹。因此/PHPAPI是文件夹,recording.mp3是它应该查找的原始文件。/PHPAPI/recording.mp3是路径。如果原始文件不存在,则从$ _GET创建它,但如果存在,则将计数器编号附加到它。 - fixnode
@ShawnMehan 我非常清晰地回答了Taluses的问题。 - fixnode
1
@Talus 这不是真的,PHP脚本会获取域名的Apache网站根目录。如果是这种情况,文件一开始就不会被创建。 - fixnode
1
再说一遍,不行。file_exists 不关心那个。URL地址里的任何内容对PHP来说都无关紧要。试试 __DIR__,它是包含当前文件的目录的完整路径,并且它不会给你 /,而是 /var/www/osbs。PHP中的所有文件函数都是大致相同的:它需要真实路径(或相对于当前脚本的路径,以简化操作)。realpath('/PHPAPI/...') 应该返回 null 或类似的东西,而不是你期望的路径,而 realpath(__DIR__ . '/PHPAPI/...') 将返回你期望的路径。file_exists 也是同样的情况。 - Talus
1
PHP是一种通用语言,您的代码可以从CLI工作,而CLI显然不知道您的文档根目录。您将了解路径与URL之间的区别。 - greg0ire
显示剩余3条评论

1
你可能会混淆文件的URL和其路径。
你的httdoc(或public_html)根目录是“/var/www/osbs/PHPAPI”。
但是你的文件系统根目录是“/'”。
尝试:
file_put_contents( __DIR__.'/'.$name, file_get_contents($_GET["RecordingUrl"]));

你的代码中存在很多不良实践


1

您忘记了使用file_put_contents()函数的路径参数。

正确的写法应该是:

file_put_contents("PHPAPI/".$name, file_get_contents($_GET["RecordingUrl"]));

或者:

file_put_contents("/var/www/osbs/PHPAPI/".$name, file_get_contents($_GET["RecordingUrl"]));

0

进行一次“小”重构:

  1. 绝对路径无处不在
  2. 透明函数,更自解释的名称,简化参数使用
  3. 防止恶意输入($_POST 真的不够用)
  4. 为什么要使用 file_put_contents() 而不是 copy()

    <?php
    
    define("SRC_PATH", "/var/www/osbs/whereverYourSrcIs/");
    define("SAVE_PATH", "/var/www/osbs/PHPAPI/");
    
    function findAvailableName($name) {
        $i = 1;
        $pathinfo = pathinfo($name);
        while(file_exists($name)) {
            $name = $pathinfo['dirname'] . '/' . $pathinfo['filename'] . "." . $i++ . "." . $pathinfo['extension'];
        }
        return $name;
    }
    
    if (isset($_GET["RecordingUrl"]) && $_GET["RecordingUrl"]) {
    
        if (strpos('/' . $_GET['RecordingUrl'] . '/', '/../') !== false) {
            die("无效输入,请勿恶意操作");
        }
    
        copy(SRC_PATH . $_GET["RecordingUrl"], findAvailableName(SAVE_PATH . "recording.mp3"));
    }
    

0

'file_exists'和其他一些文件调用,如fstat,都被PHP缓存。这在file_exists的手册中有记录。当文件不存在时,您的第一个调用将被保存并在后续调用中返回。在调用之间使用'clearstatcache()'清除缓存。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接