记录一个大文件的分片上传,利用前端分片,对接阿里云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 协议》,转载必须注明作者和本文链接
在等待的日子里,努力工作,刻苦读书,锻炼身体,谦卑做人,养得深根,日后才能枝叶茂盛
阿神
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 6

前端可以直传oss,可以不用经过后端

9个月前 评论
轻语过往 9个月前
ORH (作者) 9个月前
她来听我的演唱会 9个月前

前端直传OSS比较好;经过后端接口,还占用服务器资源。

9个月前 评论