如何用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页面而不是文件。
带进度条的核心实现逻辑
要让浏览器显示下载进度,关键在于:
- 给浏览器返回准确的
Content-Length(文件总大小),这样浏览器能计算已下载比例。 - 分块输出文件内容,并且实时刷新输出缓冲,不让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
相关产品推荐
相关产品推荐

