PHP 中的可恢复文件上传:优雅地处理大文件上传





5.00/5 (2投票s)
您是否曾经在 PHP 中遇到过大文件上传的困境?在上传过程中遇到中断时,您是否想过能否在中断的地方继续上传,而无需重新上传全部数据?如果这听起来很熟悉,那么请继续阅读。
文件上传是我们几乎所有现代 Web 项目中常见的任务。有了各种可用的工具,用任何语言实现文件上传功能并不难。但是,当涉及到大文件上传时,事情会变得有点复杂。
假设您正在尝试上传一个相当大的文件。您已经等待了一个多小时,上传进度已经达到 90%。然后,突然,您的连接中断了,或者浏览器崩溃了。上传被中止,您需要从头开始。很沮丧,不是吗?更糟糕的是,如果您使用的是慢速连接,就像世界上许多地方一样,无论您尝试多少次,每次只能上传上传的第一部分。
在这篇文章中,我们将通过使用 tus 协议分块上传文件,来探讨如何在 PHP 中解决这个问题。
https://www.youtube.com/watch?v=sHep0dLbMd4
什么是 tus?
Tus 是一个基于 HTTP 的 可恢复文件上传的开放协议。可恢复意味着在发生任何中断的情况下,我们可以从中断的地方继续,而无需重新上传全部数据。中断可能是用户主动暂停,也可能是由于网络问题或服务器故障等意外情况。
Tus 协议于 2017 年 5 月被 Vimeo 采纳。
为什么选择 tus?
引用 Vimeo 的博客
我们决定在我们的上传堆栈中使用 tus,因为 tus 协议以一种简洁开放的方式标准化了文件上传过程。这种标准化将允许 API 开发人员更多地关注他们应用程序特定的代码,而不是上传过程本身。
以这种方式上传文件的另一个主要好处是,您可以从笔记本电脑开始上传,甚至可以从手机或任何其他设备继续上传同一个文件。这是增强用户体验的好方法。
入门
让我们开始添加我们的依赖项。
$ composer require ankitpokhrel/tus-php
tus-php 是一个框架无关的纯 PHP 服务器和客户端实现,支持 tus 可恢复上传协议 v1.0.0。
https://github.com/ankitpokhrel/tus-php
更新:Vimeo 现在在其 官方 Vimeo API PHP 库 的 v3 版本中使用了 TusPHP。
创建服务器以处理我们的请求
一个简单的服务器看起来是这样的
// server.php$server = new \TusPhp\Tus\Server('redis');
$response = $server->serve();$response->send();exit(0); // Exit from current PHP process.
您需要配置您的服务器以响应特定的端点。例如,在 Nginx 中,您可以这样做:
# nginx.conf
location /files {
try_files $uri $uri/ /path/to/server.php?$query_string;
}
假设我们的服务器 URL 是 http://server.tus.local。那么,根据上面的 nginx 配置,我们可以通过 http://server.tus.local/files. 访问我们的 tus 端点。
现在,我们可以使用以下 RESTful 端点来工作。
# Gather information about server's current configuration
OPTIONS /files# Check if the given upload is valid
HEAD /files/{upload-key}# Create a new upload
POST /files# Resume upload created with POST
PATCH /files/{upload-key}# Delete the previous upload
DELETE /files/{upload-key}
有关端点的更多信息,请查看 协议详情。
如果您正在使用 Laravel 等框架,则无需修改服务器配置,可以在您的 框架 路由文件中定义所有基于 tus 的端点的路由。我们将在另一个教程中详细介绍这一点。
使用 tus-php 客户端处理上传
服务器就位后,客户端可用于分块上传文件。让我们开始创建一个简单的 HTML 表单以从用户那里获取输入。
<form action="upload.php" method="post" enctype="multipart/form-data">
<input type="file" name="tus_file" id="tus-file" />
<input type="submit" value="Upload" />
</form>
表单提交后,我们需要按照几个步骤来处理上传。
- 创建 tus-php 客户端对象
// Tus client$client = new \TusPhp\Tus\Client('http://server.tus.local');
上述代码中的第一个参数是您的 tus 服务器端点。
- 使用文件元数据初始化客户端
为了保持上传的唯一性,我们需要使用一个标识符来识别后续请求中的上传。为此,我们将不得不生成一个唯一的上传密钥,该密钥可用于以后恢复上传。您可以显式提供上传密钥,也可以让系统自行生成密钥。
// Set upload key and file meta$client->setKey($uploadKey) ->file($_FILES['tus_file']['tmp_name'], 'your file name');
如果您不显式提供上传密钥,则上述步骤将如下所示:
$client->file($_FILES['tus_file']['tmp_name'], 'your file name'); $uploadKey = $client->getKey(); // Unique upload key
- 上传一个块
// $chunkSize is size in bytes, // i.e. 5000000 for 5 MB$bytesUploaded = $client->upload($chunkSize);
下次,当您想上传另一个块时,可以使用相同的上传密钥继续。
// To resume upload in next request$bytesUploaded =
// $client->setKey($uploadKey)->upload($chunkSize);
上传完成后,服务器会根据校验和验证上传,以确保上传的文件没有损坏。服务器默认使用 sha256
算法来验证上传。
上面演示视频的完整实现可以在 这里 找到。
使用 tus-js-client 处理上传
Uppy 是由 tus 协议的开发者开发的时尚、模块化的文件上传插件。您可以使用 uppy 将官方的 tus-js-client 与 tus-php 服务器无缝集成。这意味着我们正在使用服务器的 PHP 实现和客户端的 JS 实现。
uppy.use(Tus, {
endpoint: 'https://server.tus.local/files/', // your tus endpoint
resume: true,
autoRetry: true,
retryDelays: [0, 1000, 3000, 5000]
})
分块上传
tus-php 服务器支持 连接扩展,并且能够将多个上传连接成一个,从而使客户端能够执行并行上传并上传非连续的块。
分块上传的完整示例可以在 此处 找到。
结束语
tus-php 项目本身仍处于初级阶段。将来某些部分可能会发生变化。可以在 example 文件夹中找到三个不同的实现示例。欢迎您尝试并报告发现的任何问题。非常欢迎您提交拉取请求和项目建议。
祝您编码愉快!
历史
- 2019 年 9 月 23 日:初始版本