原文地址:https://prinzeugen.net/implem...php
PHP 实现远程下载文件到服务端并非什么新鲜玩意,用 cURL
、file_get_contents
、fopen
等都可以轻易实现。前端
可是这几种常规的方法都是在一个线程内下载文件,等文件下载完毕之后才能返回 HTTP 响应。所形成的结果就是用户在页面上点击「下载到服务器」按钮后,会看到空白页和加载的小菊花转啊转,转很久以后才出现「下载成功」的页面。git
固然,我上面所举例的状况是只使用纯粹的表单 POST 发送请求的状况。如今的话就算再不济也通常会使用 ajax
发送请求,而后在前台放个加载动画,等收到下载成功的回应以后再进行下一步操做。github
可是!即便是去掉了恶心的且须要等待的空白页,这样作仍是对用户体验有很差的影响。没有具体的下载进度,只有一个一直转呀转的菊花图,估计挺多用户都没法坐和放宽吧(至少对于我来讲是这样的)ajax
而我一个 PHP 项目的一键更新系统正好须要重构,遂研究了如何在 PHP 做为后端时显示远程文件下载进度条,并捣鼓出了个像样的解决方案,在这里分享一下。后端
也许你在搜索「PHP 下载 进度条」的时候会看到有些文章使用 PHP 的输出控制函数(flush
之类的)控制缓冲区来实现进度条。可是——浏览器
那都是狗屁!缓存
没有人能够保证用户的 PHP 关闭了默认开启的 output buffering
,也没法保证 浏览器 / Web Server 不对脚本输出进行缓存。若是上述二者其中之一处于开启状态的话,你就会喜闻乐见的发现本应该慢慢增加的进度条会在等待完漫长的 xx 秒后一会儿蹦到 100%_(由于控制前端进度条长度的语句被缓存起来,在脚本执行结束后一并发送了,而不是一块一块地传给浏览器)_。服务器
关于上面缓冲区控制的进度条就是辣鸡的更多讨论能够查看文章底部的参考连接。架构
闲话休提。那么咱们该如何实现下载进度条的更新呢?
首先经过后端一点点输出控制进度条语句的方案已经 PASS 了,那么咱们很天然的就会想到——
在前端设置一个定时器,Ajax 轮循下载进度并更新页面上的进度条。
知道了原理以后,咱们先来考虑下总体的架构与步骤。
用户点击「下载」按钮,前端展现出进度条,并 ajax 发送 prepare-download
的请求;
后端收到请求,进行远程下载的准备工做 —— 准备好远程文件连接、临时文件存放位置以及文件的大小,并返回给浏览器;
前端拿到文件大小等信息后,发送真正的 start-download
请求(这个请求耗时可能会很长),并启动轮循的计时器;
计时器启动后,每隔一段时间发送 get-file-size
请求,获取当前临时文件的大小,计算进度后更新进度条;
直到下载完成。
下面给出先后端代码的实例。
代码照例放在 Gist 上,加载不出自行解决:
https://gist.github.com/print...
示例代码使用了 ?action=xxx
的 Query String 形式来区分不一样的指令,这些请酌情修改。和我业务逻辑有关的一些关键函数都被我替换为浅显易懂的名字(譬如 get_remote_file_url
)了,须要你本身去替换实现。
https://gist.github.com/print...
实例代码用了 fopen
和循环 fwrite
写入一个 chunk 的数据到临时文件,这是借鉴了 KODExplorer 远程下载的函数,在此致谢。另外也有经过 curl_setopt($ch, CURLOPT_FILE, $fp);
给 cURL 设置一个文件句柄的方法,可是我没有测试成功,但愿各位也能试一试。
以上。