php 制作一个临时目录以将 zipfile 解压缩到
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1707801/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Making a temporary dir for unpacking a zipfile into
提问by Arto Uusikangas
I have a script that checks a zipfile containing a number of matching PDF+textfiles. I want to unpack, or somehow read the textfiles from the zipfile, and just pick out some information from the textfile to see that the file version is correct.
我有一个脚本来检查包含许多匹配的 PDF+文本文件的 zip 文件。我想解压缩,或者以某种方式从 zipfile 中读取文本文件,然后从文本文件中挑选一些信息来查看文件版本是否正确。
I was looking at the tempnam()function to find an equivalent to make a tempdir, but maybe someone has a better solution for the problem.
我正在查看该tempnam()函数以找到一个等效的函数来创建一个 tempdir,但也许有人对这个问题有更好的解决方案。
The indexfile looks something like this. (->is for TAB char).
I have made the function to extract the version from the textfile and to check if its correct already, its only the unpacking, tmpdir or some other solution im looking for.
索引文件看起来像这样。(->用于 TAB 字符)。我已经创建了从文本文件中提取版本并检查它是否已经正确的功能,它只是我正在寻找的解包、tmpdir 或其他一些解决方案。
1000->filename->file version->program version->customer no->company no->distribution
2000->pagenumber->more info->more info->...
回答by Mario Mueller
quite easy (I took partly it from the PHP manual):
很简单(我从 PHP 手册中摘取了一部分):
<?php
function tempdir() {
$tempfile=tempnam(sys_get_temp_dir(),'');
// you might want to reconsider this line when using this snippet.
// it "could" clash with an existing directory and this line will
// try to delete the existing one. Handle with caution.
if (file_exists($tempfile)) { unlink($tempfile); }
mkdir($tempfile);
if (is_dir($tempfile)) { return $tempfile; }
}
/*example*/
echo tempdir();
// returns: /tmp/8e9MLi
See: https://www.php.net/manual/en/function.tempnam.php
请参阅:https: //www.php.net/manual/en/function.tempnam.php
Please look at Will's solution below.
请看下面 Will 的解决方案。
=> My answer should not be the accepted answer anymore.
=> 我的答案不再是公认的答案。
回答by Will
So I first found a post by Ron Korving on PHP.net, which I then modified to make a bit safer (from endless loops, invalid characters, and unwritable parent dirs) and use a bit more entropy.
所以我首先在 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;
}
?>
So, let's try it out:
那么,让我们试一试:
<?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));
?>
Result:
结果:
/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)
回答by zelanix
Another option if running on linux with mktempand access to the execfunction is the following:
如果在 linux 上运行mktemp并访问该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)
?>
This avoids the potential (although unlikely) race issue above and has the same behavior as the tempnamfunction.
这避免了上述潜在(虽然不太可能)的竞争问题,并具有与tempnam函数相同的行为。
回答by Haravikk
I wanted to add a refinement to @Mario Mueller's answer, as his is subject to possible race conditions, however I believe the following should not be:
我想对@Mario Mueller 的回答进行改进,因为他的回答受可能的竞争条件的影响,但我认为以下内容不应该是:
function tempdir(int $mode = 0700): string {
do { $tmp = sys_get_temp_dir() . '/' . mt_rand(); }
while (!@mkdir($tmp, $mode));
return $tmp;
}
This works because mkdirreturns falseif $tmpalready exists, causing the loop to repeat and try another name.
这是有效的,因为如果已经存在则mkdir返回,导致循环重复并尝试另一个名称。false$tmp
Note also that I've added handling for $mode, with a default that ensures the directory is accessible to the current user only, as mkdir's default is 0777otherwise.
还要注意,我已经添加了 处理$mode,默认情况下确保只有当前用户可以访问目录,否则mkdir默认情况下0777。
It is strongly advised that you use a shutdown function to ensure the directory is removed when no longer needed, even if your script exits by unexpected means*. To facilitate this, the full function that I use does this automatically unless the $auto_deleteargument is set to false.
强烈建议您使用关闭功能来确保在不再需要目录时删除该目录,即使您的脚本以意外方式退出*。为方便起见,我使用的完整函数会自动执行此操作,除非将$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;
}
This means that by default any temporary directory created by tempdir()will have permissions of 0700and will be automatically deleted (along with its contents) when your script ends.
这意味着默认情况下,任何由 by 创建的临时目录tempdir()都将拥有 权限,0700并且会在您的脚本结束时自动删除(连同其内容)。
NOTE: *This may not be the case if the script is killed, for this you might need to look into registering a signal handler as well.
注意:*如果脚本被终止,情况可能并非如此,为此您可能还需要考虑注册信号处理程序。
回答by Romain
The "mkdir" function raises a warning if the directory already exists, so you can catch this using "@mkdir" and avoid any race condition:
如果目录已经存在,“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;
}
回答by Amado Martinez
There are a lot of overkill answers to this question. One simple answer would be:
这个问题有很多矫枉过正的答案。一个简单的答案是:
$tempdir = tempnam(sys_get_temp_dir()) . 'dir';
mkdir($tempdir);
- Obtain a temporary file name.
- Create the directory (append a suffix to temp file, to avoid file name collision.)
- Done.
- 获取临时文件名。
- 创建目录(在临时文件后附加一个后缀,以避免文件名冲突。)
- 完毕。
回答by jap1968
Another possibility is to use the temporal file as a kind of semaphore to guarantee the unicity of the directory name. Then, create a directory whose name is based on the file name.
另一种可能性是使用临时文件作为一种信号量来保证目录名称的唯一性。然后,创建一个以文件名命名的目录。
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);
}
/* ************************************************************************** */

