记录一个大文件的分片上传,利用前端分片,对接阿里云oss
最近做了一个大文件分片上传的功能,记录下
1.首先是安装阿里云oss扩展
composer require aliyuncs/oss-sdk-php
去阿里云oss获取配置文件
AccessKey ID = ***
AccessKey Secret = ***
Bucket名称 = ***
Endpoint = ***
2.前端上传,对文件进行分片
<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('选择本地文件')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="file" id="fileInput">
<div>
<a href="#" onclick="startUpload()"><i class="fa fa-upload"></i>选择完点击上传(请等待上传完成)</a>
</div>
<div id="progress" style="margin-top:10px;"></div>
</div>
</div>
<div class="form-group layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-primary btn-embossed disabled">{:__('OK')}</button>
</div>
</div>
</form>
<script>
let chunkSize = 5 * 1024 * 1024; // 分片大小5MB
let uploadId = '';
let objectName = '';
let parts = [];
const CHUNK_SIZE = 5 * 1024 * 1024; // 分片阈值5MB
async function startUpload() {
const file = document.getElementById('fileInput').files[0];
if (!file) {
layer.msg('请选择文件', {icon: 1});
}
// 根据文件大小选择上传方式
if (file.size <= CHUNK_SIZE) {
await directUpload(file);
} else {
await chunkedUpload(file);
}
}
// 开始上传
async function directUpload(file) {
const formData = new FormData();
formData.append('file', file);
formData.append('file_name', file.name);
// 显示进度条
const progressBar = document.getElementById('progress');
progressBar.innerHTML = '上传进度:0%';
try {
const res = await fetch('/api/directUpload', {
method: 'POST',
body: formData,
});
const data = await res.json();
if (data.code === 1) {
progressBar.innerHTML = '上传进度:100%';
$("#c-name").val(data.name);
$("#c-fullurl").val(data.fullurl);
layer.msg('上传成功', {icon: 1});
} else {
throw new Error(data.msg);
}
} catch (error) {
progressBar.innerHTML = '上传失败';
console.error('直接上传失败:', error);
}
}
async function chunkedUpload(file) {
const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
let uploadedChunks = 0;
// 初始化分片上传
const initRes = await fetch('api/initUpload', {
method: 'POST',
body: JSON.stringify({filename: file.name}),
headers: {'Content-Type': 'application/json'}
});
const initData = await initRes.json();
if (initData.code !== 1) return alert('初始化失败');
const {uploadId, objectName} = initData;
const parts = [];
// 上传所有分片
for (let i = 0; i < totalChunks; i++) {
const start = i * CHUNK_SIZE;
const end = Math.min(start + CHUNK_SIZE, file.size);
const chunk = file.slice(start, end);
const formData = new FormData();
formData.append('part', chunk);
formData.append('uploadId', uploadId);
formData.append('objectName', objectName);
formData.append('partNumber', i + 1);
const uploadRes = await fetch('api/uploadPart', {
method: 'POST',
body: formData
});
const partData = await uploadRes.json();
if (partData.code === 1) {
parts.push({
PartNumber: partData.partNumber,
ETag: partData.etag
});
uploadedChunks++;
// 更新进度
const progress = (uploadedChunks / totalChunks * 100).toFixed(2);
document.getElementById('progress').innerHTML = `上传进度:${progress}%`;
}
}
// 合并分片
const completeRes = await fetch('api/completeUpload', {
method: 'POST',
body: JSON.stringify({
uploadId,
objectName,
parts: JSON.stringify(parts)
}),
headers: {'Content-Type': 'application/json'}
});
const completeData = await completeRes.json();
if (completeData.code === 1) {
$("#c-name").val(completeData.name);
$("#c-fullurl").val(completeData.fullurl);
layer.msg('上传成功', {icon: 1});
} else {
layer.msg('上传失败' + completeData.msg, {icon: 2});
}
}
</script>
2.后端控制器
<?php
namespace app\****;
use OSS\Core\OssException;
use OSS\OssClient;
class Attachment
{
// 初始化分片上传
public function initUpload()
{
$object = 'uploads/' . date('Ymd') . '/' . $this->request->post('filename');
try {
$ossClient = new OssClient(
config('alioss.accessKeyId'),
config('alioss.accessKeySecret'),
config('alioss.endpoint')
);
$uploadId = $ossClient->initiateMultipartUpload(config('alioss.bucket'), $object);
return json([
'code' => 1,
'uploadId' => $uploadId,
'objectName' => $object
]);
} catch (OssException $e) {
return json(['code' => 0, 'msg' => $e->getMessage()]);
}
}
// 上传分片
public function uploadPart()
{
$data = $this->request->post();
try {
$ossClient = new OssClient(
config('alioss.accessKeyId'),
config('alioss.accessKeySecret'),
config('alioss.endpoint')
);
$options = [
OssClient::OSS_FILE_UPLOAD => $_FILES['part']['tmp_name'],
OssClient::OSS_PART_NUM => $data['partNumber'],
OssClient::OSS_CHECK_MD5 => true
];
$etag = $ossClient->uploadPart(
config('alioss.bucket'),
$data['objectName'],
$data['uploadId'],
$options
);
return json([
'code' => 1,
'etag' => $etag,
'partNumber' => $data['partNumber']
]);
} catch (OssException $e) {
return json(['code' => 0, 'msg' => $e->getMessage()]);
}
}
// 完成上传
public function completeUpload()
{
$data = $this->request->post();
try {
$ossClient = new OssClient(
config('alioss.accessKeyId'),
config('alioss.accessKeySecret'),
config('alioss.endpoint')
);
$result = $ossClient->completeMultipartUpload(
config('alioss.bucket'),
$data['objectName'],
$data['uploadId'],
json_decode($data['parts'], true)
);
return json([
'code' => 1,
'url' => $result['oss-request-url'],
'name' => pathinfo($data['objectName'], PATHINFO_FILENAME),
'fullurl' => strstr($result['oss-request-url'], '?', true),
]);
} catch (OssException $e) {
return json(['code' => 0, 'msg' => $e->getMessage()]);
}
}
// 直接上传完整文件
public function directUpload()
{
try {
$ossClient = new OssClient(
config('alioss.accessKeyId'),
config('alioss.accessKeySecret'),
config('alioss.endpoint')
);
$file = $_FILES['file'];
$file_name = $this->request->request('file_name', '');
$object = 'uploads/' . date('Ymd') . '/' . $file['name'];
$result = $ossClient->uploadFile(
config('alioss.bucket'),
$object,
$file['tmp_name']
);
return json([
'code' => 1,
'url' => $result['oss-request-url'],
'name' => pathinfo($file_name, PATHINFO_FILENAME),
'fullurl' => $result['oss-request-url'],
]);
} catch (OssException $e) {
return json(['code' => 0, 'msg' => $e->getMessage()]);
}
}
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: