readfile 遇到 Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 185679872 bytes)

异常代码:

function download($name, $path) {
    // 不限制脚本的最大执行时间,避免在指定的时间内文件未下载完成。
    set_time_limit(0);
    
    $file_name = urldecode($name);
    $file_path = urldecode($path);
    $file_name = iconv("utf-8", "gb2312//IGNORE", $file_name);
    
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header("Content-type:text/html;charset=utf-8");
    header('Content-Disposition: attachment; filename=' . $file_name);
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Pragma: public');
    
    readfile($file_path);
}

download($_GET['file_name'], $_GET['file_path']);

首先可以使用 fread 逐步读取,对于大文件进度比较慢:

function download($name, $path) {
    // 不限制脚本的最大执行时间,避免在指定的时间内文件未下载完成。
    set_time_limit(0);
    
    $file_name = urldecode($name);
    $file_path = urldecode($path);
    $file_name = iconv("utf-8", "gb2312//IGNORE", $file_name);
    
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header("Content-type:text/html;charset=utf-8");
    header('Content-Disposition: attachment; filename=' . $file_name);
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Pragma: public');
    
    // readfile($filePath);

    ob_start();
    
    $fp = fopen($filePath, "r+");
    $buffer = 1024 * 1024 * 4; // 4m
    while (! feof($fp)) {
        $file_data = fread($fp, $buffer);
        echo $file_data;
            
        ob_flush();
        flush();
            
        // 可以进行限速
        // sleep(1);
    }
    ob_end_clean();
        
    fclode($fp);
}

download($_GET['file_name'], $_GET['file_path']);

查看官方手册,手册中指出:

readfile 读取文件并写入到输出缓冲

readfile() 自身不会导致任何内存问题。如果出现内存不足的错误,使用 ob_get_level() 确保输出缓存已经关闭

最后调整为:

function download($name, $path) {
    // 不限制脚本的最大执行时间,避免在指定的时间内文件未下载完成。
    set_time_limit(0);
    
    $file_name = urldecode($name);
    $file_path = urldecode($path);
    $file_name = iconv("utf-8", "gb2312//IGNORE", $file_name);
    
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header("Content-type:text/html;charset=utf-8");
    header('Content-Disposition: attachment; filename=' . $file_name);
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Pragma: public');
        
    if (ob_get_level()) {
        ob_end_flush();
    }
    
    readfile($file_path);
}

download($_GET['file_name'], $_GET['file_path']);

发表评论

您的电子邮箱地址不会被公开。