0

0

PHP如何获取文件扩展名_PHP从文件名中提取扩展名的几种方法

冰火之心

冰火之心

发布时间:2025-09-15 20:08:01

|

331人浏览过

|

来源于php中文网

原创

最稳妥获取PHP文件扩展名的方法是使用pathinfo()函数,它能准确解析路径并返回扩展名,适用于多点、无扩展名及隐藏文件;相较之下,strrpos()与substr()组合或explode()分割字符串的方法虽可行,但需手动处理边界情况,易出错且不推荐用于复杂场景。

php如何获取文件扩展名_php从文件名中提取扩展名的几种方法

PHP要获取文件扩展名,最稳妥和推荐的方法是使用

pathinfo()
函数,它能非常方便地从一个完整的文件路径中解析出包括扩展名在内的各种信息。当然,如果你只是想简单地从文件名字符串中截取,也可以结合
strrpos()
substr()
,或者利用
explode()
函数进行字符串分割。每种方法都有其适用场景和需要注意的地方。

解决方案

在我看来,处理文件路径和文件名,

pathinfo()
绝对是PHP提供的一个利器。它不仅仅是用来获取扩展名那么简单,更像是一个文件路径的“瑞士军刀”。

1. 使用

pathinfo()
函数(推荐且最全面)

pathinfo()
函数可以返回一个包含文件路径信息的关联数组,或者根据指定的
options
返回特定的字符串。获取扩展名时,我们通常会用到
PATHINFO_EXTENSION

立即学习PHP免费学习笔记(深入)”;

<?php
$filename1 = "my_document.pdf";
$filename2 = "archive.tar.gz";
$filename3 = "image.jpeg.webp";
$filename4 = "document_without_extension";
$filename5 = ".htaccess"; // 隐藏文件

// 获取完整信息
$info1 = pathinfo($filename1);
echo "文件名: {$filename1}, 扩展名: " . ($info1['extension'] ?? '无') . "\n";
// 输出: 文件名: my_document.pdf, 扩展名: pdf

// 直接获取扩展名
$ext2 = pathinfo($filename2, PATHINFO_EXTENSION);
echo "文件名: {$filename2}, 扩展名: {$ext2}\n";
// 输出: 文件名: archive.tar.gz, 扩展名: gz

$ext3 = pathinfo($filename3, PATHINFO_EXTENSION);
echo "文件名: {$filename3}, 扩展名: {$ext3}\n";
// 输出: 文件名: image.jpeg.webp, 扩展名: webp

$ext4 = pathinfo($filename4, PATHINFO_EXTENSION);
echo "文件名: {$filename4}, 扩展名: " . ($ext4 ?: '无') . "\n";
// 输出: 文件名: document_without_extension, 扩展名: 无

$ext5 = pathinfo($filename5, PATHINFO_EXTENSION);
echo "文件名: {$filename5}, 扩展名: " . ($ext5 ?: '无') . "\n";
// 输出: 文件名: .htaccess, 扩展名: htaccess
?>

可以看到,

pathinfo()
在处理多点文件名、无扩展名文件以及隐藏文件时都表现得非常智能和准确。它总是能找到最后一个点号后面的部分作为扩展名。

2. 使用

strrpos()
substr()
(手动控制,灵活但需谨慎)

如果你对性能有极致要求(虽然对于这种操作来说,性能差异通常可以忽略不计),或者需要更精细地控制“点”的位置,可以手动使用字符串函数。

<?php
$filename1 = "my_document.pdf";
$filename2 = "archive.tar.gz";
$filename3 = "document_without_extension";
$filename4 = ".htaccess";

function getExtensionManual($filename) {
    $pos = strrpos($filename, '.');
    if ($pos === false) {
        return ''; // 没有点,或者点在开头且不是隐藏文件
    }
    // 检查是否是隐藏文件且没有其他扩展名,例如 ".bashrc"
    if ($pos === 0 && strlen($filename) > 1) { // 如果点是第一个字符,且文件名不止一个点
        return substr($filename, 1); // 返回点后面的内容作为扩展名 (如 .htaccess -> htaccess)
    }
    // 正常情况,返回最后一个点后面的内容
    return substr($filename, $pos + 1);
}

echo "文件名: {$filename1}, 扩展名: " . getExtensionManual($filename1) . "\n"; // pdf
echo "文件名: {$filename2}, 扩展名: " . getExtensionManual($filename2) . "\n"; // gz
echo "文件名: {$filename3}, 扩展名: " . getExtensionManual($filename3) . "\n"; // (空)
echo "文件名: {$filename4}, 扩展名: " . getExtensionManual($filename4) . "\n"; // htaccess
?>

这个方法需要自己处理各种边界情况,比如文件名中没有点、点在开头等等。相较于

pathinfo()
,它显得复杂且容易出错。

3. 使用

explode()
end()
(简单粗暴,但不推荐用于所有场景)

这是一种非常直观的方法,但它在处理一些特殊文件名时会遇到问题。

<?php
$filename1 = "my_document.pdf";
$filename2 = "archive.tar.gz";
$filename3 = "document_without_extension";
$filename4 = ".htaccess"; // 隐藏文件
$filename5 = "test."; // 以点结尾的文件名

function getExtensionExplode($filename) {
    $parts = explode('.', $filename);
    if (count($parts) > 1 && end($parts) !== '') { // 确保有多个部分且最后一个部分不为空
        return end($parts);
    }
    return '';
}

echo "文件名: {$filename1}, 扩展名: " . getExtensionExplode($filename1) . "\n"; // pdf
echo "文件名: {$filename2}, 扩展名: " . getExtensionExplode($filename2) . "\n"; // gz
echo "文件名: {$filename3}, 扩展名: " . getExtensionExplode($filename3) . "\n"; // (空)
echo "文件名: {$filename4}, 扩展名: " . getExtensionExplode($filename4) . "\n"; // htaccess (这里可能会误判,如果期望是空)
echo "文件名: {$filename5}, 扩展名: " . getExtensionExplode($filename5) . "\n"; // (空)
?>

这个方法在处理像

archive.tar.gz
这样的文件名时没问题,但遇到
document_without_extension
.htaccess
这种,它的逻辑就可能与预期不符。特别是
.htaccess
,它会将
htaccess
视为扩展名,这在某些语境下可能是对的,但在另一些语境下,比如期望一个真正的“文件类型”扩展名时,就可能出问题。

为什么直接使用字符串分割(如
explode
)有时会出问题?

嗯,说实话,

explode
结合
end
来获取扩展名,在很多简单的场景下确实能用,但它隐藏着不少坑。我个人觉得,这种方法最大的问题在于它对文件名的结构做了过于简单的假设。

想象一下,一个文件名可能是

my.document.v1.pdf
explode('.')
会把它分成
['my', 'document', 'v1', 'pdf']
,然后
end()
得到
pdf
,这没问题。但如果文件名是
document_without_extension
explode('.')
得到
['document_without_extension']
end()
还是
document_without_extension
。这时候,你就需要额外判断
count($parts)
是否大于1,否则就会把整个文件名当成扩展名。

更麻烦的是那些以点开头的隐藏文件,比如

.htaccess
explode('.')
会得到
['', 'htaccess']
。如果你的逻辑是
end($parts)
,那就会得到
htaccess
。这在某些场景下可能是你想要的,但如果你的系统需要严格区分“无扩展名”和“有扩展名但以点开头”,这种处理方式就显得不够精确。

还有一种情况是,文件名可能包含路径,比如

/var/www/html/image.jpg
explode('.')
会把路径也考虑进去,结果就更混乱了。虽然标题是“从文件名中提取”,但实际开发中,我们经常是从完整路径中提取。

所以,

explode
的问题主要在于:

  1. 无法正确处理无扩展名的文件: 容易将整个文件名误判为扩展名。
  2. 对多点文件名的处理是“取最后一个”: 这和
    pathinfo()
    一致,但其它的边界条件处理起来更繁琐。
  3. 对隐藏文件的处理可能不符合预期:
    .htaccess
    这样的文件,它的“扩展名”究竟是
    htaccess
    还是没有扩展名,不同业务场景有不同定义。
  4. 不具备路径解析能力: 如果传入的是完整路径,它无法区分文件名和路径。

这些细微的差异,在日常开发中很容易被忽视,直到出现一个特殊文件名导致系统报错或逻辑混乱时,才发现问题的根源。这就是为什么我更倾向于

pathinfo()
,它在设计之初就考虑到了这些复杂性。

pathinfo()
函数除了扩展名还能获取哪些信息?它在实际开发中有哪些高级用法?

pathinfo()
函数远不止获取扩展名那么简单,它能把一个完整的文件路径拆解成好几个有用的部分,这在文件操作中非常方便。除了
PATHINFO_EXTENSION
,它还能获取:

一点PPT
一点PPT

一句话生成专业PPT,AI自动排版配图

下载
  • dirname
    (目录名):
    文件所在的目录路径。
  • basename
    (基本名):
    文件的完整名称,包括扩展名。
  • filename
    (文件名):
    文件的名称,不包含扩展名。

如果我们不指定

options
参数,
pathinfo()
默认会返回一个包含这四个键的关联数组。

<?php
$filepath = "/var/www/uploads/documents/report_2023.pdf";
$fileinfo = pathinfo($filepath);

echo "完整路径: {$filepath}\n";
echo "目录名 (dirname): " . $fileinfo['dirname'] . "\n";      // /var/www/uploads/documents
echo "基本名 (basename): " . $fileinfo['basename'] . "\n";    // report_2023.pdf
echo "文件名 (filename): " . $fileinfo['filename'] . "\n";    // report_2023
echo "扩展名 (extension): " . $fileinfo['extension'] . "\n";  // pdf

// 你也可以单独获取某个部分
echo "单独获取文件名: " . pathinfo($filepath, PATHINFO_FILENAME) . "\n"; // report_2023
?>

在实际开发中的高级用法:

  1. 文件重命名与归档: 假设你需要将用户上传的文件按照日期和原文件名进行归档,同时防止文件名冲突。

    <?php
    $uploadedFile = "/tmp/php_upload_temp_file.tmp"; // 假设这是临时上传文件
    $originalFilename = "My Important Document.v2.docx";
    
    $info = pathinfo($originalFilename);
    $newFilename = $info['filename'] . '_' . date('YmdHis') . '.' . $info['extension'];
    $destinationDir = "/var/www/uploads/" . date('Y/m/d'); // 按日期分目录
    
    if (!is_dir($destinationDir)) {
        mkdir($destinationDir, 0755, true); // 递归创建目录
    }
    $destinationPath = $destinationDir . '/' . $newFilename;
    
    // move_uploaded_file($uploadedFile, $destinationPath);
    echo "文件将保存到: " . $destinationPath . "\n";
    // 比如:/var/www/uploads/2023/10/27/My Important Document.v2_20231027103000.docx
    ?>

    这里

    pathinfo()
    帮我们轻松地获取了文件名和扩展名,方便我们构建新的文件名。

  2. 文件类型验证(初步): 虽然更安全的做法是检查 MIME 类型,但

    pathinfo()
    提供的扩展名可以作为第一层快速筛选。

    <?php
    $allowedExtensions = ['jpg', 'jpeg', 'png', 'gif'];
    $uploadedFilename = "image.JPG"; // 用户上传的文件名
    
    $ext = strtolower(pathinfo($uploadedFilename, PATHINFO_EXTENSION)); // 转换为小写进行比较
    
    if (in_array($ext, $allowedExtensions)) {
        echo "文件扩展名合法。\n";
    } else {
        echo "文件扩展名不合法!\n";
    }
    ?>

    注意:这只是初步验证,恶意用户可能上传一个名为

    virus.jpg
    但内容是可执行脚本的文件。

  3. 构建新的文件路径: 如果你需要将文件从一个目录移动到另一个目录,或者只是改变文件名,

    pathinfo()
    让你能轻松地组合路径。

    <?php
    $oldPath = "/data/images/thumbnails/photo.jpg";
    $newBaseName = "resized_photo.png";
    
    $info = pathinfo($oldPath);
    $newPath = $info['dirname'] . '/' . $newBaseName;
    echo "新路径: " . $newPath . "\n"; // /data/images/thumbnails/resized_photo.png
    ?>

    这比手动拼接字符串要清晰和健壮得多,尤其是在处理不同操作系统路径分隔符时(

    pathinfo
    会根据当前系统自动处理)。

总的来说,

pathinfo()
的强大在于它提供了一个标准化的方式来解析文件路径的各个组成部分,大大简化了文件操作的逻辑,减少了手动字符串处理可能带来的错误。

处理文件名时,如何兼顾安全性与性能,避免潜在的风险?

在处理文件名,尤其是用户上传的文件名时,安全性绝对是首要考虑的。性能当然重要,但通常在文件操作中,I/O本身的开销远大于文件名解析的CPU开销,所以安全性优先级更高。

安全性考量与避免风险:

  1. 绝不相信用户上传的文件名: 这是黄金法则。用户可以上传

    ../../etc/passwd
    这样的文件名,试图进行路径遍历攻击。或者上传
    evil.php.jpg
    这样的文件,试图绕过扩展名检查。

    • 路径遍历防护: 永远不要直接使用用户提供的文件名作为文件路径的一部分。即使是

      basename()
      这样的函数,也只能提取文件名部分,不能保证其安全。在保存文件前,一定要对文件名进行清理。

    • 文件名清理: 我通常会这样做:

      • 使用
        pathinfo()
        提取原始文件名和扩展名。
      • filename
        部分进行过滤,只保留字母、数字、下划线、连字符。可以使用
        preg_replace('/[^a-zA-Z0-9_\-.]/', '', $filename)
        这样的正则表达式,或者更严格的白名单。
      • 为文件生成一个唯一且不可预测的新名称,例如使用
        uniqid()
        结合
        md5()
        random_bytes()
      • 最后将清理过的或生成的新文件名与原始扩展名拼接起来。
      <?php
      $userProvidedFilename = "../../etc/passwd.jpg"; // 恶意尝试
      $userProvidedFilename2 = "我的图片 123.png";
      
      function sanitizeAndGenerateFilename($originalFilename) {
          $info = pathinfo($originalFilename);
          $cleanFilename = preg_replace('/[^a-zA-Z0-9_\-]/', '', $info['filename']); // 只保留安全字符
          $uniqueId = uniqid('', true); // 生成唯一ID
          $extension = isset($info['extension']) ? '.' . strtolower($info['extension']) : '';
      
          // 组合成新的安全文件名
          return $cleanFilename . '_' . $uniqueId . $extension;
      }
      
      echo "原始文件名: {$userProvidedFilename} -> 安全文件名: " . sanitizeAndGenerateFilename($userProvidedFilename) . "\n";
      // 可能得到: etcpasswd_653b1b4b9e7b21.23456789.jpg
      echo "原始文件名: {$userProvidedFilename2} -> 安全文件名: " . sanitizeAndGenerateFilename($userProvidedFilename2) . "\n";
      // 可能得到: 我的图片123_653b1b4b9e7b21.23456789.png
      ?>

      这种方法能有效防止路径遍历和一些文件名注入攻击。

  2. 严格的文件类型验证(MIME 类型): 仅仅依靠文件扩展名进行文件类型验证是不可靠的,因为扩展名可以随意修改。更安全的方法是检查文件的实际 MIME 类型。

    • finfo_open()
      /
      mime_content_type()
      PHP 提供了
      finfo_open()
      (Fileinfo 扩展) 或旧的
      mime_content_type()
      函数来检测文件的 MIME 类型。这需要文件实际内容。
    <?php
    $uploadedFile = "/path/to/uploaded/image.jpg"; // 假设这是已上传到服务器的临时文件
    // 确保文件存在且可读
    if (file_exists($uploadedFile)) {
        $finfo = finfo_open(FILEINFO_MIME_TYPE); // 返回 MIME 类型
        $mimeType = finfo_file($finfo, $uploadedFile);
        finfo_close($finfo);
    
        $allowedMimeTypes = ['image/jpeg', 'image/png', 'image/gif'];
    
        if (in_array($mimeType, $allowedMimeTypes)) {
            echo "文件MIME类型合法: {$mimeType}\n";
        } else {
            echo "文件MIME类型不合法: {$mimeType}\n";
        }
    }
    ?>

    结合扩展名白名单和 MIME 类型验证,能大大提高文件上传的安全性。

  3. 存储目录的权限设置: 用户上传的文件应该存储在 Web 服务器无法直接执行的目录中。例如,不要将图片上传到 Web 根目录,而是上传到 Web 根目录之外或配置为不允许执行脚本的目录。

性能考量:

对于获取文件扩展名这个操作本身,

pathinfo()
和手动
strrpos()
+
substr()
的性能差异微乎其微,在绝大多数应用中都可以忽略不计。PHP 内部函数通常都经过高度优化。

真正的性能瓶颈往往出现在:

  • 文件I/O操作: 读取、写入、移动文件。
  • 大量的文件列表操作: 遍历大量文件或目录。
  • 图像处理: 缩放、裁剪等操作。

因此,在兼顾性能时,我们更应该关注文件存储策略、CDN 使用、文件缓存、异步处理等宏观层面,而不是在文件名解析这种微小操作上进行过度优化。选择

pathinfo()
这种清晰、健壮且功能全面的函数,其带来的开发效率和代码可维护性远超那点可以忽略不计的性能差异。

总结一下,安全是基础,性能是优化。在文件名处理上,优先使用

pathinfo()
获取信息,然后严格清理文件名、生成唯一名称、验证 MIME 类型,并将文件存储在安全的位置。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
js正则表达式
js正则表达式

php中文网为大家提供各种js正则表达式语法大全以及各种js正则表达式使用的方法,还有更多js正则表达式的相关文章、相关下载、相关课程,供大家免费下载体验。

531

2023.06.20

正则表达式不包含
正则表达式不包含

正则表达式,又称规则表达式,,是一种文本模式,包括普通字符和特殊字符,是计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串,通常被用来检索、替换那些符合某个模式的文本。php中文网给大家带来了有关正则表达式的相关教程以及文章,希望对大家能有所帮助。

258

2023.07.05

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

766

2023.07.05

java正则表达式匹配字符串
java正则表达式匹配字符串

在Java中,我们可以使用正则表达式来匹配字符串。本专题为大家带来java正则表达式匹配字符串的相关内容,帮助大家解决问题。

219

2023.08.11

正则表达式空格
正则表达式空格

正则表达式空格可以用“s”来表示,它是一个特殊的元字符,用于匹配任意空白字符,包括空格、制表符、换行符等。本专题为大家提供正则表达式相关的文章、下载、课程内容,供大家免费下载体验。

357

2023.08.31

Python爬虫获取数据的方法
Python爬虫获取数据的方法

Python爬虫可以通过请求库发送HTTP请求、解析库解析HTML、正则表达式提取数据,或使用数据抓取框架来获取数据。更多关于Python爬虫相关知识。详情阅读本专题下面的文章。php中文网欢迎大家前来学习。

293

2023.11.13

正则表达式空格如何表示
正则表达式空格如何表示

正则表达式空格可以用“s”来表示,它是一个特殊的元字符,用于匹配任意空白字符,包括空格、制表符、换行符等。想了解更多正则表达式空格怎么表示的内容,可以访问下面的文章。

245

2023.11.17

正则表达式中如何匹配数字
正则表达式中如何匹配数字

正则表达式中可以通过匹配单个数字、匹配多个数字、匹配固定长度的数字、匹配整数和小数、匹配负数和匹配科学计数法表示的数字的方法匹配数字。更多关于正则表达式的相关知识详情请看本专题下面的文章。php中文网欢迎大家前来学习。

547

2023.12.06

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

26

2026.03.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PHP课程
PHP课程

共137课时 | 13.5万人学习

JavaScript ES5基础线上课程教学
JavaScript ES5基础线上课程教学

共6课时 | 11.3万人学习

PHP新手语法线上课程教学
PHP新手语法线上课程教学

共13课时 | 1.0万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号