Создание временного каталога для распаковки zipfile в
У меня есть script, который проверяет zip файл, содержащий несколько подходящих файлов PDF + textfiles. Я хочу распаковать или каким-то образом прочитать текстовые файлы из zip файла и просто выбрать некоторую информацию из текстового файла, чтобы убедиться, что версия файла верна.
Я искал функцию tempnam()
, чтобы найти эквивалент для создания tempdir, но, возможно, у кого-то есть лучшее решение проблемы.
Индексный файл выглядит примерно так. (->
для TAB char).
Я сделал функцию для извлечения версии из текстового файла и для проверки ее правильности, ее только распаковка, tmpdir или какое-либо другое решение, которое они ищут.
1000->filename->file version->program version->customer no->company no->distribution
2000->pagenumber->more info->more info->...
Ответы
Ответ 1
довольно легко (я частично взял его из руководства 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
Смотрите: http://de.php.net/manual/en/function.tempnam.php
Помните, что в Linux-системе файл и каталог теоретически одно и то же.
Остерегайтесь системы windows;)
Ответ 2
Еще одна опция, если выполняется работа с 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)
?>
Это позволяет избежать потенциальной (хотя и маловероятной) проблемы гонки выше и имеет такое же поведение, как функция tempnam
.
Ответ 3
Поэтому я впервые нашел сообщение Рона Корвига на PHP.net, которое затем было изменено, чтобы сделать его более безопасным (от бесконечных циклов, недопустимых символов и недопустимых родительских директорий) и использовать немного больше энтропии.
<?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, '/');
/* 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;
}
/* Attenot 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', $dir, $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)
Ответ 4
Я хотел добавить уточнение к ответу @Mario Mueller, так как он подвержен возможным условиям гонки, однако я считаю, что следующее не должно быть:
function tempdir(int $mode = 0700): string {
do { $tmp = sys_get_temp_dir() . '/' . mt_rand(); }
while ([email protected]($tmp, $mode));
return $tmp;
}
Это работает, потому что mkdir
возвращает false
если $tmp
уже существует, заставляя цикл повторяться и попробовать другое имя.
Обратите также внимание на то, что я добавил обработку в $mode
с по умолчанию, которая гарантирует, что каталог доступен только для текущего пользователя, так как mkdir
умолчанию - 0777
.
Настоятельно рекомендуется использовать функцию выключения, чтобы убедиться, что каталог удален, когда он больше не нужен, даже если ваш скрипт выходит неожиданными средствами *. Чтобы облегчить это, полная функция, которую я использую, делает это автоматически, если аргументу $auto_delete
не присвоено $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 ([email protected]($tmp, $mode));
if ($auto_delete) {
register_shutdown_function(function() use ($tmp) { destroydir($tmp); });
}
return $tmp;
}
Это означает, что по умолчанию любой временный каталог, созданный tempdir()
будет иметь разрешения 0700
и будет автоматически удален (вместе с его содержимым), когда ваш скрипт закончится.
ПРИМЕЧАНИЕ. * Это может быть не так, если сценарий убит, для этого вам, возможно, потребуется также изучить регистратор сигналов.
Ответ 5
На этот вопрос много ответов. Один простой ответ:
$tempdir = tempnam(sys_get_temp_dir()) . 'dir';
mkdir($tempdir);
- Получить временное имя файла.
- Создайте каталог (добавьте суффикс в файл temp, чтобы избежать столкновения имени файла.)
- Готово.
Ответ 6
Функция "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;
}
Ответ 7
Другая возможность - использовать временный файл как своего рода семафор, чтобы гарантировать однозначность имени каталога. Затем создайте каталог, имя которого основано на имени файла.
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);
}
/* ************************************************************************** */