处理大文件上传是网络开发中的一个常见挑战,由于服务器限制或网络不稳定等因素,常常会导致文件上传超时或上传失败。文件切片/分块上传提供了一种稳健的解决方案,它将大文件分解成较小的、易于管理的片(块),然后按顺序上传。这种方法不仅能提高文件上传的可靠性,还能实现暂停、恢复和重试功能。本教程将为您讲解使用 PHP 在服务器端实现分块文件上传的方法。

先决条件

  • PHP 和 JavaScript 基础知识
  • 一台已安装并配置好 PHP 与 WEB 服务的服务器
  • 熟悉客户端文件上传机制

步骤 1:编写客户端上传代码

在服务端处理上传之前,您需要先准备客户端代码,以便将文件分割成块并发送。虽然本文的重点是 PHP 端,但了解客户端逻辑也至关重要。您可以使用 JavaScript 的Blob.slice()方法分割文件,并使用FormData通过 AJAX 发送文件块。可以参考本站的JS 切片/分块上传大文件一文,该文中已给出了相关的前端示例代码,需要注意的是,要将代码中的/upload-endpoint修改为你的 php 处理网址。

步骤 2:在服务器上接收数据块

客户端发送的每个分块都需要由服务器接收并临时存储,直到文件的所有部分都上传完毕。下面是一个简单的 PHP 脚本,用于处理接收到的数据块:

<?php

$targetDir = "uploads/chunks"; // 文件分块存储目录
$targetFile = $targetDir . '/' . basename($_FILES["file"]["name"]);

// 确认目录存在,否则创建目录并赋予权限
if (!file_exists($targetDir)) {
    mkdir($targetDir, 0777, true);
}

// 将上传的片段移至存储目录
if (move_uploaded_file($_FILES["file"]["tmp_name"], $targetFile)) {
    echo "The file " . htmlspecialchars(basename($_FILES["file"]["name"])) . " has been uploaded.";
} else {
    echo "码农资源网[www.codesou.cn]提示:抱歉,上传文件时出现错误。";
}

该脚本假定每个数据块都是作为一个单独的请求接收的。它将接收到的数据块移动到指定目录。实际开发过程中,你应该增加一个索引机制来识别每一个切片/分块,通常是通过额外的 POST 数据(如文件标识符、块索引)来处理。

步骤 3:合并文件块

所有文件块上传完成后,下一步需要将它们合并生成原始文件。这可以由一个单独的请求触发,表明上传过程已经完成,也可以通过检查是否已收到所有预期的文件块来自动检测。

本文只分享在 PHP 中合并数据块的方法:

<?php

$targetDir = "uploads/chunks";
$finalFile = "uploads/final/" . $_POST['fileName']; // 最终的文件路径和文件名称

// 打开最终文件句柄
$fp = fopen($finalFile, 'wb');

// 假设文件块按顺序命名(如:file.part1、file.part2......)
for ($i=0; $i < $_POST['totalChunks']; $i++) {
    $chunkFile = $targetDir . '/' . $_POST['fileName'] . ".part" . $i;

    // 读写当前数据块
    $chunk = file_get_contents($chunkFile);
    fwrite($fp, $chunk);

    // 可选项:合并后删除文件切片/块
    unlink($chunkFile);
}

// 关闭文件句柄
fclose($fp);

echo "上传及合并完成";

该脚本假定所有数据块按顺序排列,然后根据顺序读取并将其内容写入最终文件。实际开发过程中根据客户端的实现情况调整命名约定即可。

步骤 4:安全和验证

处理文件上传需要慎重考虑安全性和数据验证:

  • 在客户端和服务器同时端验证文件类型,防止恶意上传。
  • 对中断的上传实施错误处理,包括清理部分上传。
  • 通过.htaccess限制或将其置于网络根目录之外,确保临时存储目录的安全。