引言
在处理大规模数据传输,尤其是视频、大型文档等超大文件时,传统的单次HTTP请求方式容易遇到诸多问题,如请求大小限制、超时以及传输中断。为了解决这些问题,采用大文件分片上传和断点续传技术成为了一种有效的方法。本文将详细介绍如何使用PHP结合阿里云OSS服务来实现这一功能,并提供完整的代码示例。
一、原理概览
大文件分片上传的原理是将一个大的文件分割成若干个小块(或称为“片段”),然后分别上传这些小块。这样做的好处包括但不限于:
- 减少失败风险:如果某个片段上传失败,只需重新上传该片段而非整个文件。
- 提高效率:多个片段可以并行上传,加速整体传输过程。
- 优化资源利用:通过控制每个片段的大小,可以更好地适应网络状况及服务器配置限制。
二、环境配置
Nginx & PHP设置
为了支持大文件上传,首先需要调整Nginx和PHP的相关配置以允许更大的请求体大小。以下是一个基本的配置例子:
# Nginx配置
client_max_body_size 2G;
; PHP配置
upload_max_filesize = 2G
post_max_size = 2G
请根据实际情况调整具体数值。
前端准备
前端方面推荐使用WebUploader
库进行文件选择与上传进度管理。它提供了强大的功能支持,比如拖拽、预览以及多文件并发上传等特性。
HTML结构
<div class="layui-input-block layui-upload">
<button type="button" class="layui-btn layui-btn-normal" id="chooseList">选择文件</button>
<div class="layui-upload-list">
<table class="layui-table">
<thead>
<tr>
<th>文件名</th>
<th>大小</th>
<th>上传进度</th>
<th>操作</th>
</tr>
</thead>
<tbody id="uploadList"></tbody>
</table>
</div>
</div>
<script src="__STATIC__/admin/js/sparkmd5/sparkmd5.js?v={$version}"></script>
JavaScript逻辑
layui.use(['form', 'upload', 'element'], function() {
var form = layui.form,
upload = layui.upload,
element = layui.element,
$ = layui.$;
upload.render({
elem: '#chooseList',
url: '../Ajax/multiupload',
accept: 'file',
auto: false,
choose: function(obj) {
// 文件选择后的处理逻辑...
},
done: function(res) {
// 分片上传成功后回调
},
error: function(index) {
// 出现错误时回调
}
});
// 分片上传函数
function multiupload(progressTimer, data, file, partSize, key, fileName, fileExt, hash_name, obj) {
// 实现细节...
}
// 计算文件MD5值
function getmd5(file, chunkSize) {
return new Promise((resolve, reject) => {
let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
let chunks = Math.ceil(file.size / chunkSize);
let currentChunk = 0;
let spark = new SparkMD5.ArrayBuffer();
let fileReader = new FileReader();
fileReader.onload = function(e) {
spark.append(e.target.result);
currentChunk++;
if (currentChunk < chunks) {
loadNext();
} else {
resolve(spark.end());
}
};
fileReader.onerror = function(e) {
reject(e);
};
function loadNext() {
let start = currentChunk * chunkSize;
let end = start + chunkSize;
if (end > file.size) {
end = file.size;
}
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
}
loadNext();
});
}
});
后端实现
后端主要负责接收前端发送过来的分片数据,并调用阿里云OSS API完成实际的数据存储工作。这里我们自定义了一个名为multiupload
的方法来替代原有的multiuploadFile
方法,以便更好地适配我们的需求。
public function multiupload($bucket, $object, $file, $options = null)
{
$this->precheckCommon($bucket, $object, $options);
if (empty($file)) {
throw new OssException("parameter invalid, file is empty");
}
$uploadFile = OssUtil::encodePath($file);
if (!isset($options[self::OSS_CONTENT_TYPE])) {
$options[self::OSS_CONTENT_TYPE] = $this->getMimeType($object, $uploadFile);
}
if (!$options['upload_id']) {
$options['upload_id'] = $this->initiateMultipartUpload($bucket, $object, [
'Content-Type' => $options[self::OSS_CONTENT_TYPE],
'partSize' => $options['part_size']
]);
}
$up_options = [
self::OSS_FILE_UPLOAD => $uploadFile,
self::OSS_PART_NUM => $options['part_num'],
self::OSS_SEEK_TO => 0,
self::OSS_LENGTH => $options['part_size'],
self::OSS_CHECK_MD5 => false,
];
$response_upload_part = $this->uploadPart($bucket, $object, $options['upload_id'], $up_options);
if (isset($options['etag'])) {
$options['etag'][] = substr($response_upload_part, 1, -1);
} else {
$options['etag'] = substr($response_upload_part, 1, -1);
}
if ($options['status'] == 2) {
$uploadParts = [];
foreach ($options['etag'] as $i => $value) {
$uploadParts[] = [
'PartNumber' => ($i + 1),
'ETag' => $value
];
}
$completer = $this->completeMultipartUpload($bucket, $object, $options['upload_id'], $uploadParts);
$options['url'] = $completer['info']['url'];
}
return $options;
}
以上就是如何使用PHP配合阿里云OSS实现高效的大文件分片上传解决方案。希望这篇指南能够帮助您构建更加稳定可靠的文件传输系统!