PHP无法下载保存文件 配置open_basedir正常仍提示文件不存在错误
问题排查与修复方案
核心问题点
- cURL参数配置错误:
CURLOPT_RETURNTRANSFER设置为0时,curl_exec()会直接输出请求返回的图片内容,仅返回布尔值表示请求成功/失败,你拿到的$tarotcard根本不是图片二进制内容,无法写入文件。 - 请求URL格式错误:代码中
$url = "https:.gfx.tarot.com/..."写法错误,https后应该是双斜杠//而不是点,直接导致图片请求失败。 - 文件可写性判断逻辑错误:
is_writable()对不存在的文件判断会直接返回false,你还没创建$file就做判断,导致后续写入文件的代码完全不会执行,图片文件根本不会生成。 - Zip操作逻辑错误:
$zip->addFile()只是标记要添加的文件路径,不会立即把文件内容写入压缩包,你添加后立刻执行unlink($file)删除源文件,最终生成的压缩包是空的,甚至会生成失败。 - 无错误判断逻辑:
mkdir()、$zip->open()、curl_exec()等操作都没有加返回值判断,出错时无法定位问题,直接往下执行最终就会出现找不到zip文件的报错。 - 响应头拼写错误:
ContentDisposition少了横杠,正确写法是Content-Disposition,会导致下载时文件名异常。
修复后的核心代码片段
$Deck = $Decks[$_GET['rip']]; // 先校验Deck合法性避免路径穿越 if (!preg_match('/^[a-zA-Z0-9_-]+$/', $Deck)) { die("非法参数"); } $zip = new ZipArchive(); $zpath = "/tmp/tarot/".$Deck."/"; if(!file_exists($zpath)){ // 加mkdir返回判断 if (!mkdir($zpath, 0700, true)) { die("目录创建失败"); } } $zhandle = $zpath.$Deck.".zip"; if(!file_exists($zhandle)){ // 加zip打开结果判断 if ($zip->open($zhandle, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) { die("无法创建zip文件"); } for($i=0; $i<78; $i++){ // 修正URL格式 $url = "https://gfx.tarot.com/images/site/decks/".$Deck."/full_size/".$i.".jpg"; $proxy = "192.168.1.69:9050"; $cURL = curl_init($url); curl_setopt($cURL, CURLOPT_URL, $url); curl_setopt($cURL, CURLOPT_PROXY, $proxy); curl_setopt($cURL, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); curl_setopt($cURL, CURLOPT_FOLLOWLOCATION, 1); // 修正参数,开启返回内容而非直接输出 curl_setopt($cURL, CURLOPT_RETURNTRANSFER, 1); curl_setopt($cURL, CURLOPT_HEADER, 0); $tarotcard = curl_exec($cURL); // 加curl请求判断 if(curl_errno($cURL) || $tarotcard === false){ curl_close($cURL); continue; } curl_close($cURL); $file = $zpath."/".$i.".jpg"; // 改为判断目录可写,而非不存在的文件 if(is_writable($zpath)){ $fhandle = fopen($file, 'wb+'); if($fhandle){ fwrite($fhandle, $tarotcard); fclose($fhandle); } } if(file_exists($file)){ $zip->addFile($file); // 不要在这里删,等zip关闭后再统一清理 // unlink($file); } } $zip->close(); // 现在再清理临时图片文件 for($i=0; $i<78; $i++){ $file = $zpath."/".$i.".jpg"; if(file_exists($file)){ unlink($file); } } // 先判断zip是否生成成功再输出 if(file_exists($zhandle)){ header('Content-Type: application/zip'); // 修正header拼写 header('Content-Disposition: attachment; filename='.$Deck.'.zip'); header('Content-Length: '.filesize($zhandle)); readfile($zhandle); // 可选:下载后删除zip避免占用磁盘 // unlink($zhandle); }else{ die("zip文件生成失败"); } }else{ // 已有文件直接输出 header('Content-Type: application/zip'); header('Content-Disposition: attachment; filename='.$Deck.'.zip'); header('Content-Length: '.filesize($zhandle)); readfile($zhandle); } exit();
额外排查项
如果修复后还是有问题,可排查系统是否开启了systemd的PrivateTmp配置:Apache/httpd服务如果开启了PrivateTmp=true,会给服务分配独立的临时目录,你手动操作看到的/tmp和PHP进程访问的/tmp不是同一个路径,手动切换用户创建的文件PHP自然找不到。可以修改/usr/lib/systemd/system/httpd.service文件将PrivateTmp设为false,然后执行systemctl daemon-reload && systemctl restart httpd生效。
内容的提问来源于stack exchange,提问作者headrift
相关产品推荐
相关产品推荐

