不知道有没有人考虑过通过 PHP 上传大文件,比如 1GB、10GB、100GB... ,又如果你运行机器的内存只有几百 M,总之低于目标大文件的大小。
如果这是在公司业务里,大多数会定一个上传上限,比如几 MB,或者否定这个需求。
在某些业务里确实也有用到过上传大型文件的需求,网上的解决方案基本上都是趋向于通过分片上传,或者将文件直接存储到第三方云存储,系统只保存一个链接。
举一个例子,比如 WEBDAV,他对于上传他是没有分片上传这一设计的,就算你实现了分片接收文件,总不能要求所有以 webdav 协议为标准的客户端都去实现分片上传。
但在此做一个结论,在复杂的互联网网络环境里,分片上传断点续传是比较可靠稳定的。那么以下的实现仅限于网络可靠的私有网络。
首先我们需要了解,PHP 默认配置中只支持 8M的上传,要支持更大的文件,你需要将 php.ini 中 upload_max_filesize 调整到大于等于目标文件大小,才能上传这个文件,这个配置也不能灵活动态调整。
之后最重要,PHP 的请求处理机制,他会把所有 HTTP 请求头、内容完整读取到内存才会分发请求,执行脚本文件。
试想一下,如果访客上传一个 10G 文件或者更大,内存岂不是会被一次性被吞掉。这是非常不合理的,而且这段时间速度会非常慢,处于假死状态。
这点在 Golang 基础库里做的不错,它几乎可以实现接近无消耗内存上传大文件,全 CPU 操作。
我们今天采用 Swoole 实现大文件上传,swoole 也有提供创建 HTTP 服务的方法,但依然有上传大小的限制。
修改最大数据包大小参数,package_max_length,不过这个是支持动态调整的,但依然是会将数据吞到内存里。
Swoole 底层是全内存的,因此如果设置过大可能会导致大量并发请求将服务器资源耗尽。
接下来,我们通过 Swoole 创建 TCP 服务,手动完成 HTTP 协议交互,实现大文件上传。
swoole入门到实战
第一步了解下,简单实现一个 TCP 服务。
$server = new Swoole\Server('0.0.0.0', 5555); $server->on('start', function ($server) { echo "TCP Server is started at tcp://127.0.0.1:9503\n"; }); $server->on('connect', function ($server, $fd){ echo "connection open: {$fd}\n"; }); $server->on('receive', function ($server, $fd, $reactor_id, $data) { $server->send($fd, "Swoole: {$data}"); }); $server->on('close', function ($server, $fd) { echo "connection close: {$fd}\n"; }); $server->start();
将以上代码保存为 myhttp.php ,并尝试运行
php myhttp.php
此时只能还不能作为 HTTP 服务使用,只能通过 telnet 尝试