ankitpokhrel/tus-php

用于 tus 可恢复上传协议 v1.0.0 的纯 PHP 服务器和客户端

赞助包维护!
paypal.me/pokharelankit

安装次数: 4,778,073

依赖项: 21

建议者: 0

安全: 0

星星: 1,399

关注者: 33

分支: 204

开放问题: 25

v2.4.0 2024-02-17 19:37 UTC

README

PHP Version Build Status Code Coverage Scrutinizer Code Quality Downloads Software License

使用 tus 可恢复上传协议 v1.0.0 在 PHP 中进行可恢复文件上传

TusPHP Demo

Medium 文章 ⚡ Laravel & Lumen 集成 ⚡ Symfony 集成 ⚡ CakePHP 集成 ⚡ WordPress 集成

tus 是一种基于 HTTP 的可恢复文件上传协议。可恢复意味着在发生任何中断的情况下,您可以继续上次中断的地方,而无需重新上传整个数据。中断可能是有意的,如果用户想要暂停,或者在不小心遇到网络问题或服务器故障时发生。

目录

安装

使用 composer 拉取包。

$ composer require ankitpokhrel/tus-php

// Use v1 for php7.1, Symfony 3 or 4.

$ composer require ankitpokhrel/tus-php:^1.2

用法

服务器

这是一个简单服务器的样子。

// server.php

// Either redis, file or apcu. Leave empty for file based cache.
$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/ /server.php?$query_string;
}

自 nginx 1.7.11 以来,已有新的配置选项 fastcgi_request_buffering。当启用缓冲时,在将请求发送到 FastCGI 服务器之前,将从客户端读取整个请求体。禁用此选项可能有助于解决上传期间的超时问题。此外,如果您在系统 tmp 分区的磁盘空间不足时,它也有帮助。

如果您不关闭 fastcgi_request_buffering 并使用 fastcgi,您将无法继续上传,因为 nginx 不会在文件上传完毕之前将请求返回给 PHP。

location ~ \.php$ {
    # ...

    fastcgi_request_buffering off; # Disable request buffering

    # ...
}

可以在 这里 找到示例 nginx 配置。

Apache
# .htaccess

RewriteEngine on

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^files/?(.*)?$ /server.php?$1 [QSA,L]

默认的最大上传大小为 0,这意味着没有限制。您可以根据以下说明设置最大上传大小。

$server->setMaxUploadSize(100000000); // 100 MB in bytes

服务器和客户端的默认 redis 和文件配置分别位于 config/server.phpconfig/client.php 中。要覆盖默认配置,您可以简单地将其复制到您首选的位置并更新参数。然后您需要在做任何其他事情之前设置配置。

\TusPhp\Config::set('<path to your config>');

$server = new \TusPhp\Tus\Server('redis');

或者,您可以在服务器中设置 REDIS_HOSTREDIS_PORTREDIS_DB 环境变量来覆盖服务器和客户端的 redis 设置。

客户端

客户端可用于创建、恢复和/或删除上传。

$client = new \TusPhp\Tus\Client($baseUrl);

// Key is mandatory.
$key = 'your unique key';

$client->setKey($key)->file('/path/to/file', 'filename.ext');

// Create and upload a chunk of 1MB
$bytesUploaded = $client->upload(1000000);

// Resume, $bytesUploaded = 2MB
$bytesUploaded = $client->upload(1000000);

// To upload whole file, skip length param
$client->file('/path/to/file', 'filename.ext')->upload();

要检查文件之前是否已部分上传,可以使用getOffset方法。如果上传不存在或无效,则返回false,否则返回已上传的总字节数。

$offset = $client->getOffset(); // 2000000 bytes or 2MB

从缓存中删除部分上传。

$client->delete($key);

默认情况下,客户端使用/files作为API路径。您可以使用setApiPath方法更改它。

$client->setApiPath('/api');

默认情况下,服务器将使用sha256算法来验证上传的完整性。如果您想使用不同的哈希算法,可以使用setChecksumAlgorithm方法。要获取支持的所有哈希算法列表,可以向服务器发送OPTIONS请求。

$client->setChecksumAlgorithm('crc32');

第三方客户端库

Uppy

Uppy是由tus协议背后的同一团队开发的精美、模块化文件上传插件。您可以使用Uppy无缝地将官方tus-js-client与tus-php服务器集成。有关更多详细信息,请参阅uppy文档

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

tus-php服务器与官方tus-js-client JavaScript库兼容。

var upload = new tus.Upload(file, {
  endpoint: "/tus",
  retryDelays: [0, 3000, 5000, 10000, 20000],
  metadata: {
    name: file.name,
    type: file.type
  }
})
upload.start()

云服务提供商

许多云服务提供商实现了PHP streamWrapper接口,使我们能够使用内置的PHP函数从这些提供商存储和检索数据。由于tus-php依赖于PHP的内置文件系统函数,因此我们可以轻松地将其用于上传到支持以追加二进制模式写入的API的提供商,例如Amazon S3。以下是将文件直接上传到S3存储桶的示例实现:

// server.php
// composer require aws/aws-sdk-php

use Aws\S3\S3Client;
use TusPhp\Tus\Server;
use Aws\Credentials\Credentials;

$awsAccessKey = 'AWS_ACCESS_KEY'; // YOUR AWS ACCESS KEY
$awsSecretKey = 'AWS_SECRET_KEY'; // YOUR AWS SECRET KEY
$awsRegion    = 'eu-west-1';      // YOUR AWS BUCKET REGION
$basePath     = 's3://your-bucket-name';

$s3Client = new S3Client([
    'version' => 'latest',
    'region' => $awsRegion,
    'credentials' => new Credentials($awsAccessKey, $awsSecretKey)
]);
$s3Client->registerStreamWrapper();

$server = new Server('file');
$server->setUploadDir($basePath);

$response = $server->serve();
$response->send();

exit(0);

扩展支持

  • 创建扩展基本实现完成,并用于创建上传。目前无法延迟上传的长度。
  • 实现了终止扩展,用于终止完成和未完成的上传,使服务器能够释放使用中的资源。
  • 实现了校验和扩展,服务器将默认使用sha256算法来验证上传。
  • 实现了过期扩展,详细信息如下。
  • 此连接扩展已实现,但服务器无法处理未完成的连接。

过期

服务器能够删除过期但未完成的上传。您可以使用以下命令手动或作为cron作业删除它们。请注意,此命令检查您的缓存存储以查找过期的上传。因此,请确保在缓存过期之前运行它,否则它将找不到所有需要清除的文件。

$ ./vendor/bin/tus tus:expired --help

Usage:
  tus:expired [<cache-adapter>] [options]

Arguments:
  cache-adapter         Cache adapter to use: redis, file or apcu [default: "file"]

Options:
  -c, --config=CONFIG   File to get config parameters from.

eg:

$ ./vendor/bin/tus tus:expired redis

Cleaning server resources
=========================

1. Deleted 1535888128_35094.jpg from /var/www/uploads

您可以使用--config选项覆盖默认的redis或文件配置。

$ ./vendor/bin/tus tus:expired redis --config=<path to your config file>

连接

服务器能够将多个上传连接成一个,使客户端能够执行并行上传并上传非连续块。

// Actual file key
$uploadKey = uniqid();

$client->setKey($uploadKey)->file('/path/to/file', 'chunk_a.ext');

// Upload 10000 bytes starting from 1000 bytes
$bytesUploaded = $client->seek(1000)->upload(10000);
$chunkAkey     = $client->getKey();

// Upload 1000 bytes starting from 0 bytes
$bytesUploaded = $client->setFileName('chunk_b.ext')->seek(0)->upload(1000);
$chunkBkey     = $client->getKey();

// Upload remaining bytes starting from 11000 bytes (10000 +  1000)
$bytesUploaded = $client->setFileName('chunk_c.ext')->seek(11000)->upload();
$chunkCkey     = $client->getKey();

// Concatenate partial uploads
$client->setFileName('actual_file.ext')->concat($uploadKey, $chunkBkey, $chunkAkey, $chunkCkey);

此外,服务器还将验证校验和与合并文件,以确保文件未损坏。

事件

有时,您可能希望在上传完成或创建后执行某些操作。例如,您可能希望在上传后裁剪图像或转码文件并发送电子邮件给您的用户。您可以使用tus事件来利用这些操作。以下事件在执行的不同点由服务器分发。

响应事件

要监听事件,您可以简单地附加一个监听器到事件名称。为所有监听器创建并传递一个TusEvent实例。

$server->event()->addListener('tus-server.upload.complete', function (\TusPhp\Events\TusEvent $event) {
    $fileMeta = $event->getFile()->details();
    $request  = $event->getRequest();
    $response = $event->getResponse();

    // ...
});

或者,您还可以绑定自定义类的一些方法。

/**
 * Listener can be method from any normal class.
 */
class SomeClass
{
    public function postUploadOperation(\TusPhp\Events\TusEvent $event)
    {
        // ...
    }
}

$listener = new SomeClass();

$server->event()->addListener('tus-server.upload.complete', [$listener, 'postUploadOperation']);

中间件

您可以使用中间件来操纵服务器请求和响应。中间件可以在服务器调用实际处理方法之前运行一段代码。您可以使用中间件来验证请求、处理CORS、将IP列入白名单或黑名单等。

创建中间件

为了创建中间件,您需要实现TusMiddleware接口。处理方法提供了请求和响应对象供您操作。

<?php

namespace Your\Namespace;

use TusPhp\Request;
use TusPhp\Response;
use TusPhp\Middleware\TusMiddleware;

class Authenticated implements TusMiddleware
{
    // ...

    /**
     * {@inheritDoc}
     */
    public function handle(Request $request, Response $response)
    {
        // Check if user is authenticated
        if (! $this->user->isLoggedIn()) {
            throw new UnauthorizedHttpException('User not authenticated');
        }

        $request->getRequest()->headers->set('Authorization', 'Bearer ' . $this->user->token());
    }

    // ...
}

添加中间件

要添加中间件,从服务器获取中间件对象,并简单传递中间件类。

$server->middleware()->add(Authenticated::class, AnotherMiddleware::class);

或者,您也可以传递中间件类对象。

$authenticated = new Your\Namespace\Authenticated(new User());

$server->middleware()->add($authenticated);

跳过中间件

如果您想跳过或忽略任何中间件,可以通过使用 skip 方法来实现。

$server->middleware()->skip(Cors::class, AnotherMiddleware::class);

设置开发环境和/或本地运行示例

关于此实现的ajax示例可以在 examples/ 文件夹中找到。您可以使用以下说明使用docker构建和运行它。

Docker

请确保您的系统中已安装 dockerdocker-compose。然后,从项目根目录运行docker脚本。

# PHP7
$ make dev

# PHP8
$ make dev8

# or, without make

# PHP7
$ bin/docker.sh

# PHP8
$ PHP_VERSION=8 bin/docker.sh

现在,客户端可以通过 http://0.0.0.0:8080 访问,服务器可以通过 http://0.0.0.0:8081 访问。默认API端点是 /files,上传的文件可以在 uploads 文件夹中找到。所有docker配置都可以在 docker/ 文件夹中找到。

如果您想从零开始,可以使用以下命令。它将删除并重新创建所有容器、镜像和上传文件夹。

# PHP7
$ make dev-fresh

# PHP8
$ make dev8-fresh

# or, without make

# PHP7
$ bin/clean.sh && bin/docker.sh

# PHP8
$ bin/clean.sh && PHP_VERSION=8 bin/docker.sh

我们还提供了一些实用脚本,可以帮助您轻松地进行本地开发。有关所有可用命令的列表,请参阅 Makefile。如果您不使用 make,则可以使用以下位置的shell脚本 这里

贡献

  1. 如果您还没有安装,请安装 PHPUnitcomposer
  2. 安装依赖项
    $ make vendor
    
    # or
    
    $ composer install
  3. 使用 phpunit 运行测试
    $ make test
    
    # or
    
    $ composer test
    
    # or
    
    $ ./vendor/bin/phpunit
  4. 将更改与 PSR2 编码标准 进行验证
    # fix lint issues
    $ make lint
    
    # dry run
    $ make lint-dry

您可以使用 xdebug enablexdebug disable 在容器内启用和禁用 Xdebug

关于此项目的疑问?

请随时报告您发现的任何错误。拉取请求、问题和项目建议都十分欢迎!