26

我有一个脚本可以检查包含许多匹配的 PDF+文本文件的 zipfile。我想解压缩,或者以某种方式从 zipfile 中读取文本文件,然后从文本文件中挑选一些信息来查看文件版本是否正确。

我正在查看该tempnam()函数以找到等效于制作 tempdir 的函数,但也许有人对这个问题有更好的解决方案。

索引文件看起来像这样。(->用于 TAB 字符)。我已经创建了从文本文件中提取版本并检查它是否已经正确的功能,它只是我正在寻找的解包、tmpdir 或其他一些解决方案。

1000->filename->file version->program version->customer no->company no->distribution
2000->pagenumber->more info->more info->...
4

8 回答 8

33

很简单(我从 PHP 手册中提取了一部分):

<?php

function tempdir() {
    $tempfile=tempnam(sys_get_temp_dir(),'');
    // tempnam creates file on disk
    if (file_exists($tempfile)) { unlink($tempfile); }
    mkdir($tempfile);
    if (is_dir($tempfile)) { return $tempfile; }
}

/*example*/

echo tempdir();
// returns: /tmp/8e9MLi

见:https ://www.php.net/manual/en/function.tempnam.php

请看下面威尔的解决方案。

=> 我的答案不再是公认的答案。

于 2009-11-10T13:10:24.743 回答
19

所以我首先在 PHP.net 上找到了 Ron Korving 的一篇文章,然后我对其进行了修改以使其更安全(从无限循环、无效字符和不可写的父目录中)并使用更多的熵。

<?php
/**
 * Creates a random unique temporary directory, with specified parameters,
 * that does not already exist (like tempnam(), but for dirs).
 *
 * Created dir will begin with the specified prefix, followed by random
 * numbers.
 *
 * @link https://php.net/manual/en/function.tempnam.php
 *
 * @param string|null $dir Base directory under which to create temp dir.
 *     If null, the default system temp dir (sys_get_temp_dir()) will be
 *     used.
 * @param string $prefix String with which to prefix created dirs.
 * @param int $mode Octal file permission mask for the newly-created dir.
 *     Should begin with a 0.
 * @param int $maxAttempts Maximum attempts before giving up (to prevent
 *     endless loops).
 * @return string|bool Full path to newly-created dir, or false on failure.
 */
function tempdir($dir = null, $prefix = 'tmp_', $mode = 0700, $maxAttempts = 1000)
{
    /* Use the system temp dir by default. */
    if (is_null($dir))
    {
        $dir = sys_get_temp_dir();
    }

    /* Trim trailing slashes from $dir. */
    $dir = rtrim($dir, DIRECTORY_SEPARATOR);

    /* If we don't have permission to create a directory, fail, otherwise we will
     * be stuck in an endless loop.
     */
    if (!is_dir($dir) || !is_writable($dir))
    {
        return false;
    }

    /* Make sure characters in prefix are safe. */
    if (strpbrk($prefix, '\\/:*?"<>|') !== false)
    {
        return false;
    }

    /* Attempt to create a random directory until it works. Abort if we reach
     * $maxAttempts. Something screwy could be happening with the filesystem
     * and our loop could otherwise become endless.
     */
    $attempts = 0;
    do
    {
        $path = sprintf('%s%s%s%s', $dir, DIRECTORY_SEPARATOR, $prefix, mt_rand(100000, mt_getrandmax()));
    } while (
        !mkdir($path, $mode) &&
        $attempts++ < $maxAttempts
    );

    return $path;
}
?>

所以,让我们尝试一下:

<?php
echo "\n";
$dir1 = tempdir();
echo $dir1, "\n";
var_dump(is_dir($dir1), is_writable($dir1));
var_dump(rmdir($dir1));

echo "\n";
$dir2 = tempdir('/tmp', 'stack_');
echo $dir2, "\n";
var_dump(is_dir($dir2), is_writable($dir2));
var_dump(rmdir($dir2));

echo "\n";
$dir3 = tempdir(null, 'stack_');
echo $dir3, "\n";
var_dump(is_dir($dir3), is_writable($dir3));
var_dump(rmdir($dir3));
?>

结果:

/var/folders/v4/647wm24x2ysdjwx6z_f07_kw0000gp/T/tmp_900342820
bool(true)
bool(true)
bool(true)

/tmp/stack_1102047767
bool(true)
bool(true)
bool(true)

/var/folders/v4/647wm24x2ysdjwx6z_f07_kw0000gp/T/stack_638989419
bool(true)
bool(true)
bool(true)
于 2015-05-03T06:29:37.667 回答
12

mktemp如果在 linux 上运行并访问该功能,另一个选项exec如下:

<?php

function tempdir($dir=NULL,$prefix=NULL) {
  $template = "{$prefix}XXXXXX";
  if (($dir) && (is_dir($dir))) { $tmpdir = "--tmpdir=$dir"; }
  else { $tmpdir = '--tmpdir=' . sys_get_temp_dir(); }
  return exec("mktemp -d $tmpdir $template");
}

/*example*/

$dir = tempdir();
echo "$dir\n";
rmdir($dir);

$dir = tempdir('/tmp/foo', 'bar');
echo "$dir\n";
rmdir($dir);

// returns:
//   /tmp/BN4Wcd
//   /tmp/foo/baruLWFsN (if /tmp/foo exists, /tmp/baruLWFsN otherwise)

?>

这避免了上述潜在的(尽管不太可能)竞争问题,并且具有与函数相同的行为tempnam

于 2013-06-24T16:25:56.980 回答
7

我想对@Mario Mueller 的回答进行改进,因为他受制于可能的比赛条件,但我认为以下内容不应该是:

function tempdir(int $mode = 0700): string {
    do { $tmp = sys_get_temp_dir() . '/' . mt_rand(); }
    while (!@mkdir($tmp, $mode));
    return $tmp;
}

这是有效的,因为如果已经存在则mkdir返回,导致循环重复并尝试另一个名称。false$tmp

另请注意,我添加了对$mode的处理,默认情况下确保目录只能由当前用户访问,否则mkdir默认情况下。0777

强烈建议您使用关闭功能以确保在不再需要目录时删除该目录,即使您的脚本以意外方式退出*。为了促进这一点,我使用的完整函数会自动执行此操作,除非$auto_delete参数设置为false.

// Deletes a non-empty directory
function destroydir(string $dir): bool { 
    if (!is_dir($dir)) { return false; }

    $files = array_diff(scandir($dir), ['.', '..']);
    foreach ($files as $file) {
        if (is_dir("$dir/$file")) { destroydir("$dir/$file"); }
        else { unlink("$dir/$file"); }
    }
    return rmdir($dir); 
}

function tempdir(int $mode = 0700, bool $auto_delete = true): string {
    do { $tmp = sys_get_temp_dir() . '/' . mt_rand(); }
    while (!@mkdir($tmp, $mode));

    if ($auto_delete) {
        register_shutdown_function(function() use ($tmp) { destroydir($tmp); });
    }
    return $tmp;
}

这意味着默认情况下,由 所创建的任何临时目录tempdir()都将具有权限,0700并且会在脚本结束时自动删除(连同其内容)。

注意:*如果脚本被杀死,则可能不是这种情况,为此您可能还需要考虑注册信号处理程序。

于 2017-08-09T08:21:00.423 回答
1

如果目录已经存在,“mkdir”函数会发出警告,因此您可以使用“@mkdir”捕获它并避免任何竞争条件:

function tempDir($parent = null)
{
    // Prechecks
    if ($parent === null) {
        $parent = sys_get_temp_dir();
    }
    $parent = rtrim($parent, '/');
    if (!is_dir($parent) || !is_writeable($parent)) {
        throw new Exception(sprintf('Parent directory is not writable: %s', $parent));
    }

    // Create directory
    do  { 
        $directory = $parent . '/' . mt_rand();
        $success = @mkdir($directory);
    }
    while (!$success);

    return $directory; 
}
于 2015-12-21T05:39:09.233 回答
1

这个问题有很多矫枉过正的答案。一个简单的答案是:

$tempdir = tempnam(sys_get_temp_dir()) . 'dir';
mkdir($tempdir);
  1. 获取临时文件名。
  2. 创建目录(为临时文件添加后缀,以避免文件名冲突。)
  3. 完毕。
于 2016-07-10T12:39:27.303 回答
0

另一种可能性是使用临时文件作为一种信号量来保证目录名称的唯一性。然后,根据文件名创建一个目录。

define ('TMP_DIR', '/tmp'); // sys_get_temp_dir() PHP 5 >= 5.2.1
define ('TMP_DIR_PREFIX', 'tmpdir_');
define ('TMP_DIR_SUFFIX', '.d');

/* ************************************************************************** */

function createTmpDir() {
  $tmpFile = tempnam(TMP_DIR, TMP_DIR_PREFIX);
  $tmpDir = $tmpFile.TMP_DIR_SUFFIX;
  mkdir($tmpDir);
  return $tmpDir;
}

function rmTmpDir($tmpDir) {
  $offsetSuffix = -1 * strlen(TMP_DIR_SUFFIX);
  assert(strcmp(substr($tmpDir, $offsetSuffix), TMP_DIR_SUFFIX) === 0);
  $tmpFile = substr($tmpDir, 0, $offsetSuffix);

  // Removes non-empty directory
  $command = "rm -rf $tmpDir/";
  exec($command);
  // rmdir($tmpDir);

  unlink($tmpFile);
}

/* ************************************************************************** */
于 2014-11-04T12:19:39.940 回答
0

图一将证明简单的答案。只需将前缀设置为您的应用程序特定字符串

$tmpDir = sprintf('%s%sPREFIX-%s', sys_get_temp_dir(), DIRECTORY_SEPARATOR, mt_rand());

mkdir($tmpDir);
于 2021-07-04T17:18:34.407 回答