update 初次提交分片上传
This commit is contained in:
parent
b61865f45f
commit
7d2e98cb05
@ -48,7 +48,8 @@
|
|||||||
"vue-json-pretty": "2.4.0",
|
"vue-json-pretty": "2.4.0",
|
||||||
"vue-router": "4.4.5",
|
"vue-router": "4.4.5",
|
||||||
"vue-types": "5.1.3",
|
"vue-types": "5.1.3",
|
||||||
"vxe-table": "4.5.22"
|
"vxe-table": "4.5.22",
|
||||||
|
"hash-wasm": "^4.12.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "9.15.0",
|
"@eslint/js": "9.15.0",
|
||||||
|
104
src/components/PartUpload/index.vue
Normal file
104
src/components/PartUpload/index.vue
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a href='https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/522'>分支地址</a>
|
||||||
|
<Progress :status='uploadStatus' :percent='percent' />
|
||||||
|
<input type='file' @change='handleFileChange' />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang='ts'>
|
||||||
|
import { md5 } from 'hash-wasm';
|
||||||
|
import { httpInstance, multipartUpload, type PartUploadList } from '@/utils/partUpload';
|
||||||
|
import { message, Progress } from 'ant-design-vue';
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 直接运行项目需要放行multipart接口 否则需要token
|
||||||
|
* // @SaCheckPermission("system:oss:multipart")
|
||||||
|
@PostMapping(value = "/multipart")
|
||||||
|
@SaIgnore
|
||||||
|
public R<?> multipart(@RequestBody MultipartBo multipartBo) {
|
||||||
|
*/
|
||||||
|
const uploadDone = ref(false);
|
||||||
|
const uploadStatus = computed(() => (uploadDone.value ? 'success' : 'active'));
|
||||||
|
const percent = ref(0);
|
||||||
|
|
||||||
|
// 切片
|
||||||
|
const CHUNK_SIZE = 1024 * 1024 * 5; // 5MB
|
||||||
|
async function handleFileChange(e: any) {
|
||||||
|
// 拿到上传的文件
|
||||||
|
const file = e.target.files[0]! as File;
|
||||||
|
|
||||||
|
const fileSize = file.size;
|
||||||
|
if (fileSize <= CHUNK_SIZE) {
|
||||||
|
message.error('文件大小不能小于5MB');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const chunks: Blob[] = [];
|
||||||
|
for (let i = 0; i < file.size; i += CHUNK_SIZE) {
|
||||||
|
const chunk = file.slice(i, i + CHUNK_SIZE);
|
||||||
|
chunks.push(chunk);
|
||||||
|
}
|
||||||
|
// chunk0转uint8array
|
||||||
|
const chunk0 = await chunks[0].arrayBuffer();
|
||||||
|
const uint8array0 = new Uint8Array(chunk0);
|
||||||
|
// 第一片的md5
|
||||||
|
const firstChunkMd5 = await md5(uint8array0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化分片请求 拿到uploadId
|
||||||
|
*/
|
||||||
|
const resp = await multipartUpload({
|
||||||
|
ossStatus: 'initiate',
|
||||||
|
originalName: file.name,
|
||||||
|
md5Digest: firstChunkMd5
|
||||||
|
});
|
||||||
|
const uploadId = resp.data.data.uploadId;
|
||||||
|
|
||||||
|
const partUploadList: PartUploadList[] = [];
|
||||||
|
for (let i = 0; i < chunks.length; i++) {
|
||||||
|
const chunk = chunks[i];
|
||||||
|
// const uint8array = new Uint8Array(await chunk.arrayBuffer());
|
||||||
|
// const md5Digest = await md5(uint8array);
|
||||||
|
const uploadResp = await multipartUpload({
|
||||||
|
ossStatus: 'upload',
|
||||||
|
/***
|
||||||
|
* 大坑 这里不能传md5 否则上传失败
|
||||||
|
*/
|
||||||
|
// md5Digest,
|
||||||
|
uploadId,
|
||||||
|
partNumber: i + 1
|
||||||
|
});
|
||||||
|
|
||||||
|
// 拿到上传地址 privateUrl
|
||||||
|
const privateUrl = uploadResp.data.data.privateUrl;
|
||||||
|
const minioResp = await httpInstance.put(privateUrl, chunk, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/octet-stream'
|
||||||
|
// 'Content-MD5': md5Digest,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 从headers拿到etag
|
||||||
|
const eTag = minioResp.headers['etag'];
|
||||||
|
partUploadList.push({
|
||||||
|
partNumber: i + 1,
|
||||||
|
entryTag: eTag
|
||||||
|
});
|
||||||
|
|
||||||
|
// 上传进度
|
||||||
|
percent.value = Math.round(((i + 1) / chunks.length) * 100);
|
||||||
|
console.log(`上传进度: ${percent.value}%`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并
|
||||||
|
await multipartUpload({
|
||||||
|
ossStatus: 'complete',
|
||||||
|
uploadId,
|
||||||
|
partUploadList
|
||||||
|
});
|
||||||
|
|
||||||
|
message.success('上传成功');
|
||||||
|
}
|
||||||
|
</script>
|
74
src/utils/partUpload.ts
Normal file
74
src/utils/partUpload.ts
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
export const httpInstance = axios.create({
|
||||||
|
baseURL: '/api',
|
||||||
|
timeout: 10000
|
||||||
|
});
|
||||||
|
|
||||||
|
export interface MultipartBo {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分片类型(必传)
|
||||||
|
*/
|
||||||
|
ossStatus: 'initiate' | 'upload' | 'query' | 'complete';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件原名(分片初始化的时候使用)
|
||||||
|
*/
|
||||||
|
originalName?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于分片上传任务的 Upload ID
|
||||||
|
* 在初始化分片上传时获取,并在后续的分片上传和完成上传过程中使用
|
||||||
|
*/
|
||||||
|
uploadId?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分片编号(从1开始递增)
|
||||||
|
*/
|
||||||
|
partNumber?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容的 MD5 摘要
|
||||||
|
* initiate初始化需要第一片的md5值(或者直接计算整体的md5)用来判断断点续传,以及秒传
|
||||||
|
*/
|
||||||
|
md5Digest?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最大返回的分片数(默认为1000,最大值1000)
|
||||||
|
* 最多分片一万,一次性返回会造成前端性能问题,需要前端多次校验
|
||||||
|
*/
|
||||||
|
maxParts?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分片编号的标记,用于分页查询(默认为0,表示从第一个分片开始查询)
|
||||||
|
*/
|
||||||
|
partNumberMarker?: number;
|
||||||
|
|
||||||
|
partUploadList?: PartUploadList[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PartUploadList {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分片编号(从1开始递增)
|
||||||
|
*/
|
||||||
|
partNumber: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从上传部分的内容生成的实体标签
|
||||||
|
*/
|
||||||
|
entryTag: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Data {
|
||||||
|
filename: string;
|
||||||
|
originalName: string;
|
||||||
|
md5Digest: string;
|
||||||
|
uploadId: string;
|
||||||
|
suffix: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function multipartUpload(multipartBo: MultipartBo) {
|
||||||
|
return httpInstance.post('/resource/oss/multipart', multipartBo);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user