如何在PHP ZipArchive上传处理过程中移除__MACOSX文件夹?
如何在PHP上传流程中移除Zip归档内的__MACOSX文件夹?
嘿,我太懂你这种排查半天发现是Mac隐藏文件夹搞鬼的崩溃了!__MACOSX这个家伙确实经常在Zip上传时添乱,针对你的PHP上传流程,这里有两个实用的解决方案:
方法一:解压时过滤冗余内容(最常用)
这个思路是在解压Zip文件时,直接跳过__MACOSX文件夹及其下属的所有文件,同时也过滤掉Mac系统生成的._开头的资源文件。下面是修改后的uploadZip函数:
public function uploadZip($file) { $zip = new \ZipArchive; if (true === $zip->open($file['tmp_name'])) { // 创建临时目录存放清理后的文件 $tempDir = sys_get_temp_dir() . '/clean_zip_' . uniqid(); mkdir($tempDir); // 遍历Zip内所有条目 for ($i = 0; $i < $zip->numFiles; $i++) { $entryName = $zip->getNameIndex($i); // 跳过__MACOSX相关目录和Mac资源文件 if (str_starts_with($entryName, '__MACOSX/') || str_starts_with($entryName, '_.')) { continue; } // 将有效文件提取到临时目录 $zip->extractTo($tempDir, $entryName); } $zip->close(); // --- 这里添加你的后续业务逻辑 --- // 示例:将临时目录的内容移动到目标存储路径 $targetDir = '/path/to/your/target/directory'; $this->copyDirectory($tempDir, $targetDir); // 清理临时目录,避免占用磁盘空间 $this->deleteDirectory($tempDir); } else { throw new \Exception('无法打开上传的Zip文件,请检查文件是否损坏'); } } // 辅助函数:递归复制目录 private function copyDirectory($source, $dest) { if (!is_dir($dest)) { mkdir($dest, 0777, true); } $iterator = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST ); foreach ($iterator as $item) { $targetPath = $dest . DIRECTORY_SEPARATOR . $iterator->getSubPathName(); if ($item->isDir()) { mkdir($targetPath); } else { copy($item, $targetPath); } } } // 辅助函数:递归删除目录 private function deleteDirectory($dir) { if (!is_dir($dir)) return; foreach (array_diff(scandir($dir), ['.', '..']) as $file) { $path = $dir . DIRECTORY_SEPARATOR . $file; is_dir($path) ? $this->deleteDirectory($path) : unlink($path); } rmdir($dir); }
思路说明:
- 先创建一个临时目录用来存放过滤后的有效文件
- 遍历Zip的所有条目,通过字符串判断跳过冗余内容
- 只提取需要的文件到临时目录
- 后续业务逻辑直接基于临时目录的干净文件处理
- 最后务必清理临时目录,避免磁盘空间浪费
方法二:生成干净的新Zip文件(适合保留Zip格式的场景)
如果你的业务需要保留Zip归档格式,而不是直接解压,可以创建一个新的Zip文件,只复制有效条目进去:
public function uploadZip($file) { $zip = new \ZipArchive; if (true === $zip->open($file['tmp_name'])) { // 收集所有需要保留的条目 $validEntries = []; for ($i = 0; $i < $zip->numFiles; $i++) { $entryName = $zip->getNameIndex($i); if (!str_starts_with($entryName, '__MACOSX/') && !str_starts_with($entryName, '_.')) { $validEntries[] = $entryName; } } // 创建干净的新Zip文件 $cleanZipPath = sys_get_temp_dir() . '/clean_upload_' . uniqid() . '.zip'; $cleanZip = new \ZipArchive; if ($cleanZip->open($cleanZipPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE)) { foreach ($validEntries as $entry) { // 读取原Zip的文件内容,写入新Zip $fileContent = $zip->getFromName($entry); $cleanZip->addFromString($entry, $fileContent); } $cleanZip->close(); } $zip->close(); // --- 后续逻辑:使用干净的Zip文件 --- // 示例:将干净的Zip移动到目标存储位置 rename($cleanZipPath, '/path/to/your/storage/clean_file.zip'); } else { throw new \Exception('无法打开上传的Zip文件,请检查文件是否损坏'); } }
注意事项
- 如果你的PHP版本低于8.0,
str_starts_with函数不可用,可以用substr($entryName, 0, 9) === '__MACOSX/'和substr($entryName, 0, 2) === '_.'来替代判断 - 确保服务器的临时目录(
sys_get_temp_dir()指向的路径)有足够的磁盘空间处理上传的文件 - 所有临时文件/目录使用完后一定要清理,避免磁盘空间被占用
内容的提问来源于stack exchange,提问作者John Doe
相关产品推荐
相关产品推荐

