65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2019 年 9 月 23 日

CPOL

4分钟阅读

viewsIcon

9527

您是否曾经在 PHP 中遇到过大文件上传的困境?在上传过程中遇到中断时,您是否想过能否在中断的地方继续上传,而无需重新上传全部数据?如果这听起来很熟悉,那么请继续阅读。

文件上传是我们几乎所有现代 Web 项目中常见的任务。有了各种可用的工具,用任何语言实现文件上传功能并不难。但是,当涉及到大文件上传时,事情会变得有点复杂。

假设您正在尝试上传一个相当大的文件。您已经等待了一个多小时,上传进度已经达到 90%。然后,突然,您的连接中断了,或者浏览器崩溃了。上传被中止,您需要从头开始。很沮丧,不是吗?更糟糕的是,如果您使用的是慢速连接,就像世界上许多地方一样,无论您尝试多少次,每次只能上传上传的第一部分。

在这篇文章中,我们将通过使用 tus 协议分块上传文件,来探讨如何在 PHP 中解决这个问题。

https://www.youtube.com/watch?v=sHep0dLbMd4

PHP 中的可恢复文件上传 — 演示

什么是 tus?

Tus 是一个基于 HTTP 的 可恢复文件上传的开放协议。可恢复意味着在发生任何中断的情况下,我们可以从中断的地方继续,而无需重新上传全部数据。中断可能是用户主动暂停,也可能是由于网络问题或服务器故障等意外情况。

Tus 协议于 2017 年 5 月被 Vimeo 采纳

为什么选择 tus?

引用 Vimeo 的博客

我们决定在我们的上传堆栈中使用 tus,因为 tus 协议以一种简洁开放的方式标准化了文件上传过程。这种标准化将允许 API 开发人员更多地关注他们应用程序特定的代码,而不是上传过程本身。

以这种方式上传文件的另一个主要好处是,您可以从笔记本电脑开始上传,甚至可以从手机或任何其他设备继续上传同一个文件。这是增强用户体验的好方法。

Pic: Basic Tus Architecture

图片:基本的 Tus 架构

入门

让我们开始添加我们的依赖项。

$ 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>

表单提交后,我们需要按照几个步骤来处理上传。

  1. 创建 tus-php 客户端对象
    // Tus client$client = new \TusPhp\Tus\Client('http://server.tus.local');

    上述代码中的第一个参数是您的 tus 服务器端点。

  2. 使用文件元数据初始化客户端

    为了保持上传的唯一性,我们需要使用一个标识符来识别后续请求中的上传。为此,我们将不得不生成一个唯一的上传密钥,该密钥可用于以后恢复上传。您可以显式提供上传密钥,也可以让系统自行生成密钥。

    // 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
  3. 上传一个块
    // $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-clienttus-php 服务器无缝集成。这意味着我们正在使用服务器的 PHP 实现和客户端的 JS 实现。

uppy.use(Tus, {
  endpoint: 'https://server.tus.local/files/', // your tus endpoint
  resume: true,
  autoRetry: true,
  retryDelays: [0, 1000, 3000, 5000]
})

uppy 文档 中查看更多详细信息,以及 此处的示例实现

分块上传

tus-php 服务器支持 连接扩展,并且能够将多个上传连接成一个,从而使客户端能够执行并行上传并上传非连续的块。

https://gist.github.com/ankitpokhrel/9b8bf21bccf7d32e6ea4b306523c71d8/raw/
a88cf3effc26a988aad9ceb2298e09380f7feef5/tus-partial-upload.php

使用 tus-php 进行分块文件上传

分块上传的完整示例可以在 此处 找到。

结束语

tus-php 项目本身仍处于初级阶段。将来某些部分可能会发生变化。可以在 example 文件夹中找到三个不同的实现示例。欢迎您尝试并报告发现的任何问题。非常欢迎您提交拉取请求和项目建议。

祝您编码愉快!

历史

  • 2019 年 9 月 23 日:初始版本
© . All rights reserved.