在文章《文件太大上传不了?看这里,秒传》中,我们已经介绍了大文件(几百M或几个G)上传的方法,也就是使用分片上传,相信大家已有所了解。分片上传的原理是通过将大文件分割成一个个的小文件,来突破服务器内存和时间的限制,从而实现大文件上传的目的。大文件下载,其实跟大文件上传有异曲同工之妙,它也需要突破服务器内存和时间的限制。
上电脑传和下载之所以都要突破内存和时间的限制,其实与缓冲区有关。缓冲区是存储速度不同的设备或者优先级不同的设备之间传输数据的区域。根据操作类型的不同,缓冲区可分为输入缓冲区和输出缓冲区。所有输入或输出的内容都需要先经过缓冲区,然后才能进行后续操作。使用缓冲区,可以使进程之间的相互等待变少,从而性能更好。
在PHP中有三层缓冲区,程序缓冲区、PHP-FPM缓冲区、Webserver缓冲区(Nginx/Apache)。当我们使用echo、print等输出函数打印数据时,数据会从程序缓冲区流动到PHP-FPM缓冲区、Nginx缓冲区、浏览器缓冲区,默认情况每一层缓冲区刷新到下一层缓冲区的时间节点是该层的输出缓冲区被写满或文件被关闭。
PHP的输出缓冲区默认是不开启的。在此情况下,所有输入的数据都要立即写入磁盘,所有输出的数据都要立即从磁盘读取。由于操作磁盘比较消耗性能,同时速度又慢,这样对于大文件,那必然会产生内存溢出或超时的问题。所以,为避免此情况发生,在下载的时候,我们就需要开启输出缓冲区,以实现将文件内容一点点地读取,并且是读取一点就向客户端发送一点。
PHP输出缓冲区的开关和大小设置由php.ini控制,其中output_buffering控制输出缓冲区的开启和大小设置,implict_flush控制输出缓冲区满了是否自动刷新到下一层。当然,我们也可以使用PHP中的ob系列函数进行手动操作。ob_start是开启PHP缓冲区,ob_flush是刷新PHP缓冲区,flush是刷新Webserver的缓冲区,ob_end_flush是清空并关闭PHP缓冲区。
下面我们展示一下具体的实现示例。
这里需要注意的是,Webserver和浏览器也都有自己的输出缓冲区,一般output_buffering默认是4069字符或者更大,即输出内容必须达到4069字符Web才会刷新输出缓存到下一层。所以,为了确保使用flush函数后,内容能立即从客户端输出,输出的内容必须要等于或大于4096字符。
通过以上操作,我们就突破了服务器的内存和时间限制实现了大文件的下载了。