Pre Merge pull request !163 from AprilWind/dev

This commit is contained in:
AprilWind 2024-12-02 10:26:09 +00:00 committed by Gitee
commit 72081a00ee
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
3 changed files with 178 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,102 @@
<template>
<div>
<a href="https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/522">分支地址</a>
<!-- 使用 element-plus Progress 组件 -->
<el-progress :status="uploadStatus" :percentage="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 { ElMessage, ElProgress } from 'element-plus'; // element-plus
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) {
ElMessage.error('文件大小不能小于5MB'); // 使 element-plus ElMessage
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 uploadResp = await multipartUpload({
ossStatus: 'upload',
uploadId,
partNumber: i + 1
});
// privateUrl
const privateUrl = uploadResp.data.data.privateUrl;
const minioResp = await httpInstance.put(privateUrl, chunk, {
headers: {
'Content-Type': 'application/octet-stream'
}
});
// 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
});
ElMessage.success('上传成功'); // 使 element-plus ElMessage
}
</script>
<style scoped>
/* 你可以在这里添加一些样式 */
</style>

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);
}