1. 引言
在进行大文件上传时,常常会遇到上传速度慢或者上传失败的问题。这是因为对于大文件,一次性上传会占用大量的网络和服务器资源,造成上传速度缓慢或者超时导致上传失败。为了解决这个问题,可以通过将大文件进行分片上传的方式,将文件分成多个小块逐个上传,从而提高上传速度并且降低失败的概率。
2. 大文件分片上传的原理
大文件分片上传的原理是将文件切分成多个小块,然后逐个上传。在前端,使用JavaScript将文件切割成固定大小的块,并将这些块逐个发送到后端服务。在后端,使用PHP接收这些分片,并将它们保存到服务器的临时位置。当所有分片都上传完成后,再将这些分片重新组装成完整的文件。
3. 前端实现
3.1 文件切片
在前端可以使用FormData来上传文件。首先,需要将文件转化为Blob对象,并且设置初始的文件上传位置start。然后使用FormData.append()方法将切片后的Blob对象添加到FormData中,并通过XMLHttpRequest对象将FormData发送到后端服务。
// 文件切片
function sliceFile(file) {
var sliceSize = 1024 * 1024; // 切片大小为1MB
var chunks = Math.ceil(file.size / sliceSize);
var currentChunk = 0;
var fileReader = new FileReader();
fileReader.onload = function (e) {
// 生成切片的Blob对象
var blob = new Blob([e.target.result]);
var formData = new FormData();
formData.append('file', blob, file.name+'-'+currentChunk);
formData.append('chunks', chunks);
formData.append('currentChunk', currentChunk);
// 发送切片到后端
var xhr = new XMLHttpRequest();
xhr.open('POST', 'upload.php', true);
xhr.onload = function () {
if (currentChunk < chunks - 1) {
currentChunk++;
sliceFile(file);
}
};
xhr.send(formData);
};
fileReader.readAsArrayBuffer(file.slice(currentChunk * sliceSize, (currentChunk + 1) * sliceSize));
}
// 文件选择
var fileInput = document.getElementById('fileInput');
fileInput.onchange = function () {
var file = fileInput.files[0];
sliceFile(file);
}
3.2 进度管理
为了显示上传进度,可以利用XMLHttpRequest对象的upload.onprogress事件获取当前上传进度的百分比,并更新UI。
xhr.upload.onprogress = function (e) {
var percentage = (e.loaded / e.total) * 100;
// 更新进度条
progressBar.style.width = percentage + '%';
}
4. 后端实现
4.1 文件分片接收
在后端使用PHP编写一个接收分片的接口,可以通过$_FILES数组来获取上传的分片文件。首先判断当前上传的分片是否是最后一个分片,如果是则创建一个临时文件,将所有的分片写入到临时文件中。如果不是最后一个分片,则将分片内容追加写入到临时文件中。
$chunks = $_POST['chunks'];
$currentChunk = $_POST['currentChunk'];
$tempDir = './temp/';
$tempFile = $tempDir . $_FILES['file']['name'] . '-' . $currentChunk;
move_uploaded_file($_FILES['file']['tmp_name'], $tempFile);
if ($currentChunk == $chunks - 1) {
// 当前上传的分片为最后一个分片
$uploadDir = './upload/';
$fileName = $_FILES['file']['name'];
$uploadFile = $uploadDir . $fileName;
// 创建临时文件
$tempFile = fopen($uploadFile, 'ab');
// 将所有分片追加写入临时文件
for ($i = 0; $i < $chunks; $i++) {
$tempChunk = $tempDir . $fileName . '-' . $i;
$chunk = file_get_contents($tempChunk);
fwrite($tempFile, $chunk);
unlink($tempChunk); // 删除临时分片文件
}
fclose($tempFile);
}
4.2 文件合并
在所有分片上传完成后,需要将这些分片重新合并成完整的文件。可以通过fopen()函数创建一个新的文件,然后循环读取每个分片的内容,并将内容写入到新文件中。最后,删除临时文件夹。
$uploadDir = './upload/';
$fileName = $_FILES['file']['name'];
$uploadFile = $uploadDir . $fileName;
$tempFile = fopen($uploadFile, 'wb');
// 将分片追加写入文件
for ($i = 0; $i < $chunks; $i++) {
$tempChunk = $tempDir . $fileName . '-' . $i;
$chunk = file_get_contents($tempChunk);
fwrite($tempFile, $chunk);
unlink($tempChunk); // 删除临时分片文件
}
fclose($tempFile);
// 删除临时文件夹
rmdir($tempDir);
5. 总结
使用PHP实现大文件分片上传可以解决大文件上传速度慢或者上传失败的问题。通过将文件切分成多个小块逐个上传,并在后端将这些分片重新组装成完整的文件,可以提高上传速度并且降低失败的概率。同时,通过前端实现进度管理可以方便地显示上传进度。这种方法使得大文件上传变得更加可靠和高效。