返回

PHP修改Zip文件内文件时间戳:无需解压高效方案

php

修改 Zip 文件内文件的创建日期

修改 zip 压缩包中文件的创建或修改日期,是一个有时会遇到的需求。PHP 提供了 ZipArchive 类来处理 zip 文件,但是这个类并没有直接的方法修改文件的时间戳。本文将探讨如何实现这个目标,同时避免将压缩包内的文件写入磁盘再重新添加的操作。

问题分析

ZipArchive 类虽然功能强大,提供了添加、删除、读取 zip 文件内容的功能,但是对压缩包内文件元数据的控制有限。文件的创建时间和修改时间这类时间戳,实际上存储在 zip 文件的头部信息中。PHP 的 ZipArchive 类默认处理文件时不会暴露这个信息,也没有提供直接的接口去修改它。所以,我们需要找到另外的方法间接修改 zip 文件的元数据,即需要使用其他的方法来访问和修改文件的头部信息。

解决方案:使用命令行工具

一个行之有效的方法是通过系统命令行的 zip 工具来实现修改时间戳的目的。Linux,macOS 系统默认都有这个工具,Windows 上则可能需要安装 zip 工具。通过 Shell 命令,我们可以修改 zip 档案中单个文件的时间戳,且无需解压文件到本地磁盘再重新打包。

步骤:

  1. 构造命令行: zip 命令的 -m 选项可以在压缩包内替换现有文件,同时-U选项则可以更新时间戳。我们要先知道压缩包的路径,然后通过-U参数修改指定文件的时间戳,可以指定yyyyMMddhhmmss的格式字符串作为新的时间戳,该字符串可以根据日期需求灵活设置。
  2. 使用 shell_exec() 执行命令: PHP 的 shell_exec() 函数可以执行系统 Shell 命令。我们将构造好的 zip 命令作为参数传入这个函数。

示例代码:

<?php

$zipFile = 'path/to/your/archive.zip';
$fileInZip = 'path/to/file/in/zip.txt';
$newTimestamp = '20231026120000'; // YYYYMMDDHHMMSS 格式

// 构造 shell 命令
$command = "zip -m -U $zipFile $fileInZip -z $newTimestamp";

// 执行命令
$output = shell_exec($command);


if ($output === null) {
   echo "Error executing command";
   return ;
}
 //  注意此处不能用 == 判断, 可能是 empty str

echo "Output :";
var_dump($output);

// 这里可以增加对输出结果的判断,判断命令是否成功执行
?>

代码说明:

  • $zipFile: zip 文件的绝对路径。
  • $fileInZip: zip 文件内需要修改时间戳的文件路径。
  • $newTimestamp: 新的时间戳,格式为 YYYYMMDDHHMMSS。

这个方案不需要依赖额外PHP 扩展, 利用系统的工具就可以达到我们的目标。 另外需要注意,这里使用了 shell_exec() 函数执行外部命令, 因此在构建命令的时候注意 zippath/to/your/archive.zippath/to/file/in/zip.txt 的变量,保证不会被利用恶意执行系统命令。

安全提示

  • 输入验证: shell_exec() 可以直接调用外部程序,因此需要小心使用,必须对输入的数据进行过滤和验证,以防止注入攻击,比如用户传递恶意数据来执行其他命令。可以使用 escapeshellarg() 函数来确保变量值被正确转义。
  • 命令审计:shell_exec() 执行的命令打印到日志中可以帮助我们定位问题。
  • 错误处理: 务必添加适当的错误处理机制,比如判断 shell_exec() 函数的返回值是否为 NULL,以及命令输出内容是否包含错误信息,及时发现和修复异常。

总结

通过结合系统自带的 zip 命令行工具和 shell_exec() 函数,我们可以在不解压文件的前提下,修改 zip 文件中文件的创建或修改日期。尽管 ZipArchive 类没有直接修改时间戳的功能,这个方案依旧非常实用且高效,尤其是在只需要修改文件时间戳,无需读取或修改文件内容的时候。但需谨记加强输入验证和命令执行时的安全,防止潜在风险。