【问题标题】:Unique and temporary file names in PHP?PHP中的唯一和临时文件名?
【发布时间】:2010-10-02 09:20:44
【问题描述】:

我需要将一些文件转换为 PDF,然后将它们附加到电子邮件中。我将 Pear Mail 用于它的电子邮件方面,这很好(大部分 - 仍在解决一些问题),但作为其中的一部分,我需要创建临时文件。现在我可以使用tempnam() 函数,但听起来它在文件系统上创建了一个文件,这不是我想要的。

我只想要一个临时文件系统中的名称(使用sys_get_temp_dir()),它不会与运行同一用户多次调用该脚本的同一脚本的其他人发生冲突。

建议?

【问题讨论】:

    标签: php


    【解决方案1】:

    我过去曾使用 uniqid() 来生成唯一的文件名,但实际上并未创建文件。

    $filename = uniqid(rand(), true) . '.pdf';
    

    第一个参数可以是任何你想要的,但我在这里使用了 rand() 让它更加随机。使用设置前缀,可以进一步避免与系统中的其他临时文件发生冲突。

    $filename = uniqid('MyApp', true) . '.pdf';
    

    从那里,您只需创建文件。如果所有其他方法都失败了,请将其放入一个 while 循环中并继续生成它,直到你得到一个可以工作的。

    while (true) {
     $filename = uniqid('MyApp', true) . '.pdf';
     if (!file_exists(sys_get_temp_dir() . $filename)) break;
    }
    

    【讨论】:

    • 我喜欢在 uniquid 的第一个参数中使用随机数,但是我使用 mt_rand() 代替,因为它的速度。这对于使用 while 循环并且文件很多的地方很有帮助。
    • 我不知道在其中添加 rand() 是否一定会使它变得随机 more - uniqid 和 rand() 都被系统时间。
    • 这个while循环真的应该写成一个do-while循环。
    • 有点旧,但如果没有实施第二次故障转移中断,则应该永远称赞 while 循环。特别是在很可能是 PHP CLI 的一部分的代码(或 UI 的其他“隐藏”部分)中,我从未见过如此赞扬一段非常糟糕的代码 - 没有留下深刻的印象
    • 你应该分隔 temp_dir 和文件名:!file_exists(sys_get_temp_dir() . '/' . $filename)
    【解决方案2】:

    考虑使用 uuid 作为文件名。考虑 uniqid 函数。 http://php.net/uniqid

    【讨论】:

      【解决方案3】:

      您可以使用部分日期和时间来创建唯一的文件名,这样在多次调用时不会重复。

      【讨论】:

      • 除非你真的很忙。
      【解决方案4】:

      我推荐你使用 PHP 函数 http://www.php.net/tempnam

      $file=tempnam('tmpdownload', 'Ergebnis_'.date(Y.m.d).'_').'.pdf';
      echo $file;
      /var/www/html/tmpdownload/Ergebnis_20071004_Xbn6PY.pdf
      

      或者 http://www.php.net/tmpfile

      <?php
      $temp = tmpfile();
      fwrite($temp, "writing to tempfile");
      fseek($temp, 0);
      echo fread($temp, 1024);
      fclose($temp); // this removes the file
      ?>
      

      【讨论】:

      • 这不会很好用,因为 tempnam 会例如检测到 AbCdEf 文件不存在,因此返回该名称。但是您附加 .pdf 并且可能已经有 AbCdEf.pdf 文件(tempnam 无法检测到)
      • 这是错误的。 tempnam() 实际上创建了文件,并且 OP 将此作为不使用 tempnam 的原因。您的解决方案将失败,因为创建的文件不是您正在寻找的文件(文件 xyz 已创建,您正在寻找文件 xyz.pdf)。
      【解决方案5】:
      function gen_filename($dir) {
          if (!@is_dir($dir)) {
              @mkdir($dir, 0777, true);
          }
          $filename = uniqid('MyApp.', true).".pdf";
          if (@is_file($dir."/".$filename)) {
              return $this->gen_filename($dir);
          }
          return $filename;
      }
      

      【讨论】:

        【解决方案6】:

        说真的,使用 tempnam()。是的,这会创建文件,但这是一种非常有意的安全措施,旨在防止系统上的另一个进程“窃取”您的文件名并导致您的进程覆盖您不想要的文件。

        即,考虑这个序列:

        • 您生成一个随机名称。
        • 您检查文件系统以确保它不存在。如果是,请重复上一步。
        • 另一个邪恶的进程创建了一个与硬链接同名的文件,该文件指向邪恶先生希望您意外覆盖的文件。
        • 您打开文件,以为您是在创建文件,而不是在写入模式下打开现有文件,然后开始写入。
        • 您刚刚覆盖了一些重要的内容。

        PHP 的 tempnam() 实际上在后台调用了系统的 mkstemp(这是针对 Linux 的……用“最佳实践”函数代替其他操作系统),它经历了这样的过程:

        • 选择文件名
        • 在一个目录中创建具有限制性权限的文件,以防止其他人删除它不拥有的文件(这就是粘着位在 /var/tmp 和 /tmp 上所做的)
        • 确认创建的文件仍然具有限制性权限。
        • 如果上述任一方法失败,请使用其他名称重试。
        • 返回创建的文件名。

        现在,您可以自己做所有这些事情,但是为什么,当“适当的功能”完成创建安全临时文件所需的一切时,并且几乎总是涉及为你。

        例外:

        • 您正在一个目录中创建一个临时文件,只有您的进程可以在该目录中创建/删除文件。
        • 创建一个随机生成的临时目录,只有您的进程可以在其中创建/删除文件。

        【讨论】:

          【解决方案7】:

          最好使用带有用户 ID 的 Unix 时间戳。

          $filename='file_'.time().'_'.$id.'.jepg';
          

          【讨论】:

            【解决方案8】:

            基于@Lusid 答案的另一种替代方案,具有最大执行时间的故障转移:

            // Max exectution time of 10 seconds.
            $maxExecTime = time() + 10; 
            $isUnique = false;
            
            while (time() !== $maxExecTime) {
                // Unique file name
                $uniqueFileName = uniqid(mt_rand(), true) . '.pdf';
                if (!file_exists(sys_get_temp_dir() . $uniqueFileName)){
                    $isUnique = true;
                    break;
                }
            }
            
            if($isUnique){
                // Save your file with your unique name
            }else{
                // Time limit was exceeded without finding a unique name
            }
            

            注意:

            我更喜欢使用mt_rand 而不是rand,因为第一个函数使用Mersenne Twister algorithm,它比第二个函数(LCG) 快。


            更多信息:

            【讨论】:

              【解决方案9】:

              我的想法是使用递归函数来查看文件名是否存在,如果存在,则迭代到下一个整数:

              function check_revision($filename, $rev){
                  $new_filename = $filename."-".$rev.".csv";
                  if(file_exists($new_filename)){
                      $new_filename = check_revision($filename, $rev++);
                  }
                  return $new_filename;
              }
              
              $revision = 1;
              $filename = "whatever";
              $filename = check_revision($filename, $revision);
              

              【讨论】:

                【解决方案10】:

                2020 年更新

                hash_file('md5', $file_pathname)

                这样可以防止重复。

                【讨论】:

                • 是吗?当两个用户上传同名文件时,文件路径上的 md5 哈希值仍然相同。所以这根本不是唯一的。
                猜你喜欢
                • 2012-01-23
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多