PHP大文件下载:xsendfile配置及空文件问题求助
关于X-Sendfile下载0字节空文件的问题排查与修复
一、核心代码错误:Zip文件重复创建导致损坏
你的get_album_tracks函数中,把ZipArchive的创建逻辑放到了foreach($result1 as $r1)循环内部,这会导致每次循环都重新创建并覆盖zip文件,最终zip文件要么仅保留最后一个文件,要么因重复打开/关闭操作导致文件损坏,出现0字节的情况。
修复后的get_album_tracks函数:
function get_album_tracks($promo_code, $release_title) { include_once "server/connect.php"; if(isset($_SESSION['currentUser']) === true){ $folderPath = 'audio/'. $release_title; // 创建文件夹(加判断避免重复创建报错) if(!is_dir($folderPath)) { mkdir($folderPath); } // 用参数绑定防止SQL注入,原写法存在注入风险 $query1= $conn->prepare('SELECT * FROM `promo_track_details` where `promo_code` = ? '); $query1->bindParam(1, $promo_code); $query1->execute(); $result1 = $query1->fetchAll(); // 把ZipArchive逻辑移到循环外面,只执行一次 $rootPath = realpath($folderPath); $zipFileName = 'audio/'.$release_title.'.zip'; $zip = new ZipArchive(); if($zip->open($zipFileName, ZipArchive::CREATE | ZipArchive::OVERWRITE)) { $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($rootPath), RecursiveIteratorIterator::LEAVES_ONLY ); foreach ($files as $name => $file) { if (!$file->isDir()) { $filePath = $file->getRealPath(); $relativePath = substr($filePath, strlen($rootPath) + 1); $zip->addFile($filePath, $relativePath); } } $zip->close(); } // 循环处理文件复制与重命名 foreach($result1 as $r1) { $filename = $r1['file_name']; $track_title = $r1['track_title']; $sourceFile = 'audio/'.$filename; $tempFile = $folderPath.'/'.$filename; $targetFile = $folderPath.'/'.$track_title.'.mp3'; // 复制前检查源文件是否存在 if(file_exists($sourceFile)) { copy($sourceFile, $tempFile); rename($tempFile, $targetFile); } } download_the_file_now($release_title); } }
二、修复X-Sendfile下载函数的问题
- 发送
X-Sendfile头后必须立即终止PHP执行,避免后续输出干扰; - 检查文件路径的正确性,确保
realpath返回有效路径; - 移除
Content-Length头,让Apache自动处理,避免文件大小计算错误的问题。
修复后的download_the_file_now函数:
function download_the_file_now($release_title) { $path = 'audio/'; $name = $release_title.".zip"; $file = $path.$name; if(file_exists($file)) { // 清空所有层级的输出缓冲区 while (ob_get_level()) { ob_end_clean(); } header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename='.basename($file)); header('Content-Transfer-Encoding: binary'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); $realFilePath = realpath($file); if($realFilePath) { header("X-Sendfile: ". $realFilePath); } else { // 路径无效时降级用readfile readfile($file); } // 立即终止PHP执行,防止后续输出干扰 exit; } }
三、Apache配置的正确检查
确保mod_xsendfile模块已加载:
在httpd.conf中添加(如果没有的话):LoadModule xsendfile_module modules/mod_xsendfile.so注意XAMPP中mod_xsendfile的模块文件路径是否正确,不同版本可能略有差异。
正确配置XSendFilePath:
确保路径是绝对路径,且Apache对该目录有读取权限:<IfModule mod_xsendfile.so> XSendFile On XSendFilePath "C:/xampp/htdocs/soundphiles-promo/audio" # 如果需要允许父目录,也可以添加: # XSendFilePath "C:/xampp/htdocs/soundphiles-promo" </IfModule>配置后必须重启Apache生效。
四、额外排查点
- 手动检查生成的zip文件:打开
audio/xxx.zip,确认文件大小正常且能正常解压; - 确保Apache进程对
audio目录有读取权限(Windows下可右键目录→属性→安全→添加Apache用户权限); - 关闭PHP的输出缓冲配置(php.ini中的
output_buffering设为Off),避免缓冲区残留内容干扰。
内容的提问来源于stack exchange,提问作者Bernl
相关产品推荐
相关产品推荐

