update 初次提交分片上传

This commit is contained in:
AprilWind 2024-12-02 18:22:04 +08:00
parent b61865f45f
commit 7d2e98cb05
3 changed files with 180 additions and 1 deletions

View File

@ -48,7 +48,8 @@
"vue-json-pretty": "2.4.0",
"vue-router": "4.4.5",
"vue-types": "5.1.3",
"vxe-table": "4.5.22"
"vxe-table": "4.5.22",
"hash-wasm": "^4.12.0"
},
"devDependencies": {
"@eslint/js": "9.15.0",

View 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);
}
// chunk0uint8array
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,
}
});
// headersetag
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
View 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;
/**
* 10001000
*
*/
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);
}