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

如何用PHP实现带进度条的Google Drive文件下载(附示例链接)

PHP实现带进度条的Google Drive文件下载

嘿,这个需求我之前做项目的时候刚好踩过坑,刚好可以分享下怎么用PHP实现带进度条的Google Drive文件下载!

Google Drive直链的前置注意点

  • 你提供的链接https://drive.google.com/uc?export=download&id=54fs54dg45f1f112f2是Google Drive的标准导出下载链接,但如果文件较大或者是需要权限的共享文件,Google会返回一个安全确认页面,这时候得先拿到真正的下载地址(通常带&confirm=t参数)。
  • 所以第一步必须先模拟请求,提取跳转后的真实下载链接,不然直接下载会拿到HTML页面而不是文件。

带进度条的核心实现逻辑

要让浏览器显示下载进度,关键在于:

  1. 给浏览器返回准确的Content-Length(文件总大小),这样浏览器能计算已下载比例。
  2. 分块输出文件内容,并且实时刷新输出缓冲,不让PHP把内容攒到缓冲区才发送。

完整可运行代码示例

<?php
// 目标Google Drive文件ID
$fileId = '54fs54dg45f1f112f2';
$initialUrl = "https://drive.google.com/uc?export=download&id={$fileId}";

// 第一步:获取带确认参数的真实下载链接
$curl = curl_init($initialUrl);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HEADER, true);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false); // 不自动跳转,手动提取Location头
$response = curl_exec($curl);
curl_close($curl);

// 从响应头里提取跳转后的真实链接
preg_match('/Location: (.*?)\n/', $response, $matches);
$realDownloadUrl = isset($matches[1]) ? trim($matches[1]) : $initialUrl;

// 如果链接里没有confirm参数,手动加上(跳过Google的安全确认)
if (strpos($realDownloadUrl, 'confirm=') === false) {
    $realDownloadUrl .= '&confirm=t';
}

// 第二步:获取文件的基本信息(大小、文件名)
$curl = curl_init($realDownloadUrl);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HEADER, true);
curl_setopt($curl, CURLOPT_NOBODY, true); // 只请求头,不请求内容
curl_exec($curl);
$fileSize = curl_getinfo($curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
$dispositionHeader = curl_getinfo($curl, CURLINFO_CONTENT_DISPOSITION);
curl_close($curl);

// 从Content-Disposition头里提取文件名
preg_match('/filename="(.*?)"/', $dispositionHeader, $filenameMatch);
$fileName = isset($filenameMatch[1]) ? $filenameMatch[1] : 'gdrive_download_' . time();

// 第三步:设置HTTP响应头,告诉浏览器这是一个下载文件
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $fileName . '"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . $fileSize); // 关键:告诉浏览器文件总大小

// 关闭PHP输出缓冲,确保内容实时发送
ob_end_clean();
ob_implicit_flush(true);
set_time_limit(0); // 取消脚本执行时间限制(大文件需要)

// 第四步:分块读取并输出文件内容,触发浏览器进度条
$curl = curl_init($realDownloadUrl);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, false); // 不返回内容,直接输出
curl_setopt($curl, CURLOPT_WRITEFUNCTION, function($ch, $chunk) {
    // 输出当前块内容
    echo $chunk;
    // 刷新输出缓冲,让浏览器立刻收到数据
    flush();
    ob_flush();
    // 返回已处理的字节数,curl需要这个返回值
    return strlen($chunk);
});

// 执行下载请求
curl_exec($curl);
curl_close($curl);

exit;
?>

关键代码段解释

  • 获取真实下载链接:通过curl请求初始链接,提取Location跳转头,加上confirm=t参数跳过Google的安全验证,避免下载到HTML页面。
  • 文件信息获取:用CURLOPT_NOBODY只请求响应头,拿到文件大小和文件名,这两个信息对进度条和用户体验都很重要。
  • 输出缓冲处理:ob_end_clean()清空现有缓冲,ob_implicit_flush(true)自动刷新缓冲,确保每块数据都能实时发送给浏览器。
  • 分块输出回调:CURLOPT_WRITEFUNCTION是curl的回调函数,每次读取到一块数据就输出并刷新,这样浏览器就能实时更新进度条。

常见问题排查

  • 如果进度条不显示:检查服务器是否禁用了flush()函数,比如nginx的fastcgi_buffer_size设置过大,或者apache开启了output_buffering,需要调整服务器配置。
  • 如果脚本超时:在代码开头加上set_time_limit(0);,取消PHP脚本的执行时间限制,大文件下载需要足够的时间。
  • 如果下载的是HTML页面:说明没正确获取到真实下载链接,检查正则匹配Location头的代码是否正常工作,或者手动访问初始链接看看是否需要手动确认下载。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 07:29:38