使用PhpZip生成加密压缩包下载时报Unexpected end of archive错误
问题原因
- 压缩包生成路径和下载读取路径不匹配:
saveAsFile()传入的是相对路径secure-file.zip,PHP会基于当前运行脚本的工作目录存储文件,和下载逻辑里写死的__DIR__ . "/downloads/secure-file.zip"路径不一致,实际下载时读取的大概率是目录里残留的损坏、不完整的旧压缩包文件。 - 加密方法调用顺序错误:PhpZip库的
setPassword()方法仅对调用之后添加进压缩包的文件生效,先添加文件再设置密码,会导致生成的压缩包中央目录结构异常,本身就是损坏的。 - 下载流混入额外内容:输出压缩包二进制流前没有清空输出缓冲区,PHP文件的BOM头、前置空格、Notice级报错等内容会被拼在压缩包内容前面,同时代码重复设置
Cache-Control头、在文件输出完成前就执行删除,都会导致最终下载的文件结构损坏,触发「Unexpected end of archive」错误。
修复代码
压缩包生成环节
统一存储路径,调整加密方法调用顺序,如果要打包已有zip文件使用库提供的专属方法避免结构损坏:
// 统一压缩包存储路径,和下载逻辑读取路径完全一致 $zipSavePath = __DIR__ . "/downloads/secure-file.zip"; $zipFile = new \PhpZip\ZipFile(); // 密码设置放在文件添加操作之前,确保所有待打包内容都被正确加密 $zipFile->setPassword("12345Abc"); $zipFile->addFile(__DIR__ . "/../files/oneTextFile.txt"); // 如果需要打包已有的zip文件,不要直接用addFile,改用addFileFromZip避免压缩包结构损坏 // $zipFile->addFileFromZip(__DIR__ . "/../files/oneZipFile.zip"); $zipFile->saveAsFile($zipSavePath); $zipFile->close();
压缩包下载环节
清空输出缓冲区,移除重复响应头,等文件输出完成后再删除临时文件:
$file = __DIR__ . "/downloads/secure-file.zip"; if(file_exists($file)){ // 清空所有提前输出的内容,避免BOM、空格、报错信息混入二进制流 ob_clean(); ob_end_flush(); // 统一设置响应头,移除重复配置 header('Content-Type: application/zip'); header('Content-Description: File Transfer'); header('Content-Transfer-Encoding: binary'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); header('Content-Disposition: attachment; filename="secure-file.zip"'); header('Content-Length: ' . filesize($file)); readfile($file); // 等输出流程全部结束后再删除临时压缩包,避免文件锁定导致读取不完整 register_shutdown_function(function() use ($file) { if (file_exists($file)) { unlink($file); } }); exit(); }
排查技巧:先直接打开服务器本地生成的
secure-file.zip,如果本地文件打开就报错,说明是压缩包生成环节的问题;如果本地文件正常、下载后的文件报错,对比两个文件的大小,大小不一致就是输出流混入了额外内容。
内容的提问来源于stack exchange,提问作者Peter
相关产品推荐
相关产品推荐

