You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP ZipArchive压缩后unlink临时文件报流资源无效错误

ZipArchive批量添加文件时临时文件清理报错解决方案

现有实现代码

控制器层

$zip = new ZipArchive();
$zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE);
$filesToClose = $this->advancedService->addFiles($files, $zip);
$zip->close();

foreach ($filesToClose as $file) {
    fclose($file);
    unlink(stream_get_meta_data($file)['uri']);
}
// return response

服务层addFiles方法

// addFiles function
foreach ($files as $file) {
    $fileName = 'FILE_'.$file->getId().'.'.$file->getExtension();

    $tempFile = tmpfile();
    $content = file_get_contents('uriFromSdk');
    fwrite($tempFile, $content);
    $zipArchive->addFile(stream_get_meta_data($tempFile)['uri'], $fileName);

    $filesToClose[] = $tempFile;
}

return $filesToClose;

报错信息

运行代码时触发PHP警告:
stream_get_meta_data(): supplied resource is not a valid stream resource
已知现象:调用fclose()不会触发该错误,仅调用stream_get_meta_data()时报错

约束条件

  • 禁止在$zip->close()执行完成前关闭或删除任何待压缩的临时文件,否则会导致压缩包内文件损坏

问题根因

报错是PHP临时文件资源的自动回收机制导致的:
tmpfile()创建的临时文件句柄属于PHP自动管理的资源,除了手动调用fclose()会释放资源外,当资源失去有效引用、或脚本运行到对应生命周期节点时,PHP会自动回收这类资源。你在执行$zip->close()的过程中,存入数组的临时文件资源已经被PHP底层自动释放为无效资源,此时再用无效资源调用stream_get_meta_data()获取文件路径,自然会抛出无效流资源的错误。而fclose()本身对已释放的资源做了兼容处理,传入无效资源不会触发报错,和你观察到的现象完全吻合。

修复方案

核心思路是在临时文件资源有效的时候提前缓存文件路径,不要等资源释放后再从流中读取路径,修改后的代码完全符合约束要求:

修改服务层addFiles方法

// addFiles function
foreach ($files as $file) {
    $fileName = 'FILE_'.$file->getId().'.'.$file->getExtension();

    $tempFile = tmpfile();
    $content = file_get_contents('uriFromSdk');
    fwrite($tempFile, $content);
    // 资源有效时直接获取临时文件路径并缓存
    $tempPath = stream_get_meta_data($tempFile)['uri'];
    $zipArchive->addFile($tempPath, $fileName);

    // 同时存储资源句柄和提前获取的文件路径
    $filesToClose[] = [
        'handle' => $tempFile,
        'path' => $tempPath
    ];
}

return $filesToClose;

修改控制器层清理逻辑

$zip = new ZipArchive();
$zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE);
$filesToClose = $this->advancedService->addFiles($files, $zip);
$zip->close();

foreach ($filesToClose as $tempFile) {
    // 句柄仍有效则手动关闭,已自动释放则跳过
    if (is_resource($tempFile['handle'])) {
        fclose($tempFile['handle']);
    }
    // 直接使用提前缓存的路径删除文件,无需再读取流元数据
    if (file_exists($tempFile['path'])) {
        unlink($tempFile['path']);
    }
}
// return response

该方案不会在zip打包完成前触碰临时文件,完全避免压缩包损坏的问题;同时跳过了资源释放后读取流元数据的操作,从根源解决报错。

内容的提问来源于stack exchange,提问作者talhaamir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 19:03:41