tempnam

(PHP 4, PHP 5, PHP 7, PHP 8)

tempnam创建具有唯一文件名的文件

描述

tempnam(string $directory, string $prefix): string|false

在指定的目录中创建一个具有唯一文件名的文件,其访问权限设置为 0600。如果目录不存在或不可写,tempnam() 可能会在系统的临时目录中生成一个文件,并返回该文件的完整路径,包括其名称。

参数

directory

将创建临时文件名的目录。

prefix

生成的临时文件名的前缀。

注意: 仅使用前缀的前 63 个字符,其余字符将被忽略。Windows 仅使用前缀的前三个字符。

返回值

返回新的临时文件名(带路径),如果失败则返回 false

变更日志

版本 描述
7.1.0 tempnam() 现在在回退到系统临时目录时会发出通知。

示例

示例 #1 tempnam() 示例

<?php
$tmpfname
= tempnam("/tmp", "FOO");

$handle = fopen($tmpfname, "w");
fwrite($handle, "writing to tempfile");
fclose($handle);

// 在这里做一些事情

unlink($tmpfname);
?>

注释

注意: 如果 PHP 无法在指定的 directory 参数中创建文件,它会回退到系统默认值。在 NTFS 上,如果指定的 directory 包含超过 65534 个文件,也会发生这种情况。

参见

添加一个注释

用户贡献的注释 23 个注释

78
koyama
14 年前
注意使用空 $dir 作为“技巧”来在系统临时目录中创建临时文件。

<?php
$tmpfname
= tempnam('', 'FOO'); // 不好
?>

如果 open_basedir 限制生效,此技巧将不起作用。您将收到类似以下的警告消息

警告:tempnam() [function.tempnam]:open_basedir 限制生效。
File() 不在允许的路径中:( /var/www/vhosts/example.com/httpdocs:/tmp )

有效的写法是

<?php
$tmpfname
= tempnam(sys_get_temp_dir(), 'FOO'); // 好
?>
16
php at REMOVEMEkennel17 dot co dot uk
19 年前
注意 tempnam 返回临时文件的完整路径,而不仅仅是文件名。
17
stanislav dot eckert at vizson dot de
7 年前
请注意,此函数可能会在 PHP 7.1.0 及更高版本中抛出通知。这是一个 bug 修复:https://bugs.php.net/bug.php?id=69489

您可以放置一个地址运算符 (@) 来静默通知

<?php

if ($tmp = @tempnam() !== false) {
// ...
}

?>

或者您可以尝试在 php.ini 中设置“upload_tmp_dir”设置到系统的临时文件夹路径。不确定最后一个是否可以防止通知。
24
Sebastian Kun
19 年前
如果您转到 C 函数 tempnam(3) 的 linux 手册页,您将在末尾看到“永远不要使用此函数。改为使用 mkstemp(3)。” 但是 php 的 tempnam() 函数实际上并没有使用 tmpnam(3),所以没有问题(在 Linux 下,如果可用,它将使用 mkstemp(3))。
13
Artur Graniszewski
14 年前
tempnam() 函数不支持由 stream_register_wrapper() 注册的自定义流包装器。

例如,如果您尝试在 Windows 平台上使用 tempnam(),PHP 将尝试在 %TMP% 文件夹(通常是:C:\WINDOWS\Temp)中生成唯一的文件名,而不会有任何警告或通知。

<?php

// << ...自定义流包装器在某个地方...>>

echo '<pre>';
error_reporting(E_ALL);
ini_set('display_errors', true);
clearstatcache();
stream_register_wrapper('test', 'MemoryStream');

mkdir('test://aaa');
mkdir('test://aaa/cc');
mkdir('test://aaa/dd');
echo
'PHP '.PHP_VERSION;
echo
'<br />node exists: '.file_exists('test://aaa/cc');
echo
'<br />node is writable: '.is_writable('test://aaa/cc');
echo
'<br />node is dir: '.is_dir('test://aaa/cc');
echo
'<br />tempnam in dir: '.tempnam('test://aaa/cc', 'tmp');
echo
"<br /></pre>";

?>

输出
--------------------
PHP 5.2.13
node exists: 1
node is writable: 1
node is dir: 1
tempnam in dir: C:\Windows\Temp\tmp1D03.tmp

如果您想要创建临时文件,您必须创建自己的函数(它可能会使用 opendir() 和 fopen($filename, "x") 函数)
7
Jason Pell
17 年前
我想保证文件将被创建在指定的目录中,否则该函数应该返回 FALSE,我有一个简单的函数可以工作,但我不知道它是否是一个潜在的安全问题。

function dir_tempnam($dir, $prefix)
{
$real_dir_path = realpath($dir);
if (substr($real_dir_path, -1) != '/')
$real_dir_path .= '/';

$tempfile = tempnam($real_dir_path, $prefix);
$name = basename($tempfile);

if(is_file($real_dir_path.$name))
return $name;
else
{
@unlink($name);
return FALSE;
}
}

此函数仅返回指定目录中临时文件的名称,或返回 FALSE。

显然它可以返回整个 $tempfile,但在我的情况下,我实际上需要单独的 basename 值。
6
anakin dot skyw at gmx dot de
20 年前
> 在 UNIX(你可以将文件名重命名为现有文件,因此我使用了链接)下,你必须删除链接和链接的目标。

你不能做
<?php
if ($newFileCreated) {
unlink ($sysFileName);
return
$newFileName;
}
?>
并获得与 Windows 版本相同的语义吗?
9
Ron Korving
18 年前
此函数创建一个临时目录。如果在 unlink() 和 mkdir() 之间,某个进程创建了相同的目录或文件,则之前的示例可能会出现错误。此实现也更快。

<?php
function tempdir($dir, $prefix='', $mode=0700)
{
if (
substr($dir, -1) != '/') $dir .= '/';

do
{
$path = $dir.$prefix.mt_rand(0, 9999999);
} while (!
mkdir($path, $mode));

return
$path;
}
?>
8
bishop
20 年前
在动态网站上,创建一个具有特定扩展名的临时文件是一个常见的需求。这种需求主要来自 Microsoft 浏览器,它们根据文件的扩展名识别下载文件的 MIME 类型。

没有单个 PHP 函数可以创建具有特定扩展名的临时文件名,而且正如所见,除非你使用 PHP 原子原语,否则会涉及竞态条件。

我在下面只使用原语,并利用操作系统依赖的行为来安全地创建一个具有特定后缀、前缀和目录的文件。尽情享用。

<?php
function secure_tmpname($postfix = '.tmp', $prefix = 'tmp', $dir = null) {
// 验证参数
if (! (isset($postfix) && is_string($postfix))) {
return
false;
}
if (! (isset(
$prefix) && is_string($prefix))) {
return
false;
}
if (! isset(
$dir)) {
$dir = getcwd();
}

// 查找临时名称
$tries = 1;
do {
// 获取已知且唯一的临时文件名
$sysFileName = tempnam($dir, $prefix);
if (
$sysFileName === false) {
return
false;
}

// 添加扩展名
$newFileName = $sysFileName . $postfix;
if (
$sysFileName == $newFileName) {
return
$sysFileName;
}

// 移动或将创建的临时文件指向新文件名
// 注意:如果新文件名存在,这些操作会失败
$newFileCreated = (isWindows() ? @rename($sysFileName, $newFileName) : @link($sysFileName, $newFileName));
if (
$newFileCreated) {
return
$newFileName;
}

unlink ($sysFileName);
$tries++;
} while (
$tries <= 5);

return
false;
}
?>

isWindows 函数主要留作读者的练习。以下是一个起点

<?php
function isWindows() {
return (
DIRECTORY_SEPARATOR == '\\' ? true : false);
}
?>

与 tempnam() 一样,此函数要求你稍后清理自己的文件。在 UNIX(你可以将文件名重命名为现有文件,因此我使用了链接)下,你必须删除链接和链接的目标。清理完全留给读者。
8
divinity76+yaoiporn at gmail dot com
10 年前
如果你不想自己处理删除文件,并且不需要自定义前缀,你可以使用
$file_location=stream_get_meta_data(tmpfile())['uri'];
文件将自动创建,并在脚本关闭时自动删除(感谢 tmpfile())。我发现这对 CURLOPT_COOKIEFILE 很有用(它需要文件位置,而不是句柄)
6
wapmorgan
10 年前
请注意,如果未传递第二个参数 <prefix>,则 tempnam 将返回 NULL(而不是 false)
<?php
var_dump
(tempnam(sys_get_temp_dir())); // NULL
?>
还会生成警告
警告:tempnam() 期待正好 2 个参数,给出 1 个 ... 在 php shell 代码中
4
tux ARROBA cenobioracing PUNTO com
17 年前
请注意,在 Windows NT 和其他 Windows 上,如果你有一个变量 $work_dir,它包含一个指向文档根目录(或任何其他目录)中某个目录的路径。请注意以下几点
<?php
$work_dir
= 'C:/some/path/to/document_root/dir';
file_exists($working_dir); // 返回 true
is_writable($working_dir); // 返回 true
$tempfile = tempnam($working_dir,'img');
//$temfile 现在包含一个系统范围的临时目录文件,例如 'C:/WINNT.SBS/img444.tmp',而不是我们传递给它的目录
//这是因为我们需要为 $working_dir 提供 I_USR(IIS 用户)用户写入权限,尽管根据上述函数,它似乎已经拥有权限了...
//如果你只想使用 tempnam 默认返回的系统范围的临时目录,你也需要为它提供 I_USR 用户写入权限,以便能够写入该文件...
?>
5
tomas at slax dot org
14 年前
请注意:函数不是原子的。如果多个进程同时调用同一个函数,你可能会遇到意外行为。

如果你需要自己的 tempnam 变体,请使用类似以下的内容

<?php
function tempnam_sfx($path, $suffix)
{
do
{
$file = $path."/".mt_rand().$suffix;
$fp = @fopen($file, 'x');
}
while(!
$fp);

fclose($fp);
return
$file;
}

// 像这样调用它:
$file = tempnam_sfx("/tmp", ".jpg");
?>

如果需要,你可以用其他随机名称生成器替换 mt_rand()。
4
lreilly at lanl dot gov
21 年前
小心你的正斜杠和反斜杠。像这样的看似无害的代码...

$uploaddir = "C:/Program Files/Apache Group/Apache2/htdocs/sasdap/uploads/";
$tempFile = tempnam ($uploaddir, "TMPANAL");
$fp = fopen($tmpfname, "w");
fwrite($fp, $iqdata);
//fclose($fp);

... 在回显 $tempFile 时可能会显示一些奇怪的东西。

即 /Program Files/Apache Group/Apache2/htdocs/sasdap/uploads/\TMP3D.tmp

必须... 记住... 要... 使用... 反斜杠...

- Lee P. Reilly
2
Anonymous
12 年前
tempnam 不会在未经授权的区域创建文件。
这意味着你需要对临时目录($dir)有访问权限,才能在其中创建文件。
3
phpdoc at rickbradley dot com
20 年前
下面提供的 "newtempnam" 技巧(由 "tempnam" 在 "2003 年 7 月 23 日 08:56" 发布)至少存在一个竞态条件。while 循环检查以确保要检查的文件不存在,然后继续创建文件。在存在性测试和 fopen() 调用之间,攻击者有机会创建要检查的文件。

这是一个经典的竞态条件,虽然似乎很难利用,但针对这种草率的文件创建,存在许多著名的攻击。

PHP 语言级别无法提供实现安全文件创建所需的原子原语。这进一步强调了 PHP 语言开发人员需要依赖语言的安全原语(包括 tempnam() 和 tempfile()),而不是自己编写。
3
dmhouse at gmail dot com
16 年前
Guillaume Paramelle 以下的评论值得强调:tempnam() 不会接受其第一个目录的相对路径。如果传递一个相对路径,它将(至少在 Windows XP 上)在系统临时目录中创建临时文件。

将相对路径转换为绝对路径的最简单方法是在前面加上 getcwd()。

<?php
$file
= tempnam('files/temp', 'tmp'); // 错误!
$file = tempnam(getcwd() . 'files/tmp', 'tmp') // 正确。
?>
2
hm2k at php dot net
15 年前
<?php

function tempdir($dir=false,$prefix='php') {
$tempfile=tempnam('','');
if (
file_exists($tempfile)) { unlink($tempfile); }
mkdir($tempfile);
if (
is_dir($tempfile)) { return $tempfile; }
}

/*示例*/

echo tempdir();
// 返回:/tmp/8e9MLi

?>
1
anthon at piwik dot org
14 年前
tempnam() 创建的文件将具有反映当前 umask 应用于默认值(例如,0600 或 -rw-------)的文件权限。无论 umask 是在启动 web 服务器进程之前设置,还是由对 PHP 的 umask() 函数的早期调用设置,情况都是如此。

例如,如果当前 umask 为 0022,则临时文件将以 0600(所有者可读写)权限创建。

此外,如果当前 umask 为 0222,则临时文件将以 0400(所有者只读)权限创建。(如果您的代码随后尝试打开临时文件以写入,这将是一个问题。)

重要的是要记住,umask 会撤销权限。在以上两个示例中,组、其他或执行权限都没有设置。

参见:umask()、chmod()。
0
david at frankieandshadow dot com
6 年前
Debian 9 Stretch 添加了私有临时目录,因此如果您在作为 Apache 中的 mod_php 运行时使用此函数,并期望该文件可以提供给第三方(例如在 shell 命令或从临时文件加载 MariaDB 命令中),它将不再起作用:该文件将对其他进程不可见,(尽管同一个 PHP 能够读取它。)

您需要使用第一个参数将临时文件放在特定位置(在 HTML 文档树之外),并设置权限,以便您可以与对方共享它,或者更改私有 tmp 的设置,例如

cp /lib/systemd/system/apache2.service /etc/systemd/system/
# 编辑 /etc/systemd/system/ 以将 PrivateTmp=true 更改为 false
systemctl daemon-reload
service apache2 restart

在共享服务器上选择不同的位置更好 - 私有 tmp 文件的添加是有原因的。

这是我最近在进行操作系统升级时困惑了很长时间的事情。
-4
KOmaSHOOTER at gmx dot de
18 年前
此示例创建一个名为“user.txt”的文件
在目录 www.XXXXX.XX/restricted/ 中
<?php
$tmpfname
= tempnam($_ENV["DOCUMENT_ROOT"]."/restricted", "FOO");
$handle = fopen($tmpfname, "w");
fwrite($handle, "writing to tempfile");
fclose($handle);

// 在这里执行一些操作
//echo($_ENV["DOCUMENT_ROOT"]);
copy($tmpfname,'user.txt');
?>
-4
Nick Smith
19 年前
值得注意的是,如果您提供的“dir”不存在,则它将被静默忽略,并使用系统 /tmp 目录。至少在 Linux、PHP v4.1.2 下是如此。

我有一个脚本,在关闭安全模式后似乎运行良好,但我没有意识到我的“dir”参数有拼写错误(因此文件被放在 /tmp 中),一旦安全模式打开,我开始收到错误,因为我的脚本的其他部分无法从系统 /tmp 文件夹中读取文件。
-18
Steve Clay
10 年前
您不需要 tempdir()。只需使用 tempnam() 并将其替换为具有相同名称的目录
<?php
$temp_dir
= tempnam('/tmp', 'prefix');
unlink($temp_dir);
mkdir($temp_dir);
To Top