123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- <template>
- <div class="chunk-upload">
- <el-button type="primary" @click="handleClick" :disabled="uploading">
- {{ uploading ? '上传中...' : '选择并上传文件' }}
- </el-button>
- <div v-for="(progress, index) in uploadProgressList" :key="index" class="progress-item">
- <p>File {{ index + 1 }}: {{ progress.fileName }}</p>
- <el-progress :percentage="progress.percentage"></el-progress>
- </div>
- <input type="file" ref="fileInput" @change="handleFileChange" style="display: none;" multiple />
- </div>
- </template>
- <script lang="ts">
- import { defineComponent, ref } from "vue";
- import { ElMessage } from "element-plus";
- import axios from "axios";
- import { v1 as uuidv1 } from "uuid";
- axios.defaults.baseURL = "http://10.181.7.236:9988";
- export default defineComponent({
- name: "ChunkUpload",
- props: {
- maxFileSize: {
- type: Number,
- default: 10 * 1024 * 1024, // 默认10MB
- },
- maxFiles: {
- type: Number,
- default: 3, // 默认最大上传3个文件
- },
- },
- setup(props) {
- const fileInput = ref<HTMLInputElement | null>(null);
- const uploading = ref(false);
- const uploadProgressList = ref<{ fileName: string; percentage: number }[]>([]);
- const CHUNK_SIZE = 1 * 1024 * 1024; // 每个分片的大小为 1MB
- const handleClick = () => {
- fileInput.value?.click();
- };
- const handleFileChange = async (event: Event) => {
- const files = (event.target as HTMLInputElement).files;
- if (!files || files.length === 0) {
- return;
- }
- // 检查文件数量
- if (files.length > props.maxFiles) {
- ElMessage.error(`最多只能上传 ${props.maxFiles} 个文件`);
- return;
- }
- uploading.value = true;
- uploadProgressList.value = Array.from(files).map(file => ({
- fileName: file.name,
- percentage: 0,
- }));
- try {
- for (let i = 0; i < files.length; i++) {
- const file = files[i];
- if (file.size > props.maxFileSize) {
- ElMessage.error(`文件 ${file.name} 大小不能超过 ${props.maxFileSize / (1024 * 1024)} MB`);
- continue; // 跳过这个文件继续上传下一个
- }
- const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
- const fileIdentifier = await generateFileIdentifier();
- for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
- const start = chunkIndex * CHUNK_SIZE;
- const chunk = file.slice(start, start + CHUNK_SIZE);
- await uploadChunk(chunk, chunkIndex, totalChunks, fileIdentifier);
- uploadProgressList.value[i].percentage = Math.round(((chunkIndex + 1) / totalChunks) * 100);
- }
- // 文件分片上传完成后,调用合并接口
- const uuidFilename = await mergeChunks(fileIdentifier, file.name);
- console.log("上传成功的文件 UUID 名称:", uuidFilename);
- }
- ElMessage.success("文件上传完成!");
- } catch (error) {
- ElMessage.error("文件上传失败!");
- console.error("Upload error:", error);
- } finally {
- uploading.value = false;
- }
- };
- const uploadChunk = async (
- chunk: Blob,
- chunkNumber: number,
- totalChunks: number,
- fileIdentifier: string
- ) => {
- const formData = new FormData();
- formData.append("file", chunk);
- try {
- await axios.post(`/api/file/upload/uploadfile`, formData, {
- params: {
- chunknumber: chunkNumber,
- identifier: fileIdentifier,
- },
- headers: {
- "Content-Type": "multipart/form-data",
- },
- });
- } catch (error) {
- throw new Error(`上传分片 ${chunkNumber} 失败`);
- }
- };
- const mergeChunks = async (identifier: string, filename: string) => {
- try {
- const response = await axios.post("/api/file/upload/mergefile", null, {
- params: {
- identifier: identifier,
- filename: filename,
- chunkstar: 0, // 假设所有分片的开始序号为0
- },
- });
- if (response.status !== 200) {
- throw new Error("文件合并失败");
- }
- return response.data.uuidFilename;
- } catch (error) {
- throw new Error("合并请求失败");
- }
- };
- const generateFileIdentifier = async (): Promise<string> => {
- return uuidv1(); // 生成一个固定的 UUID1
- };
- return {
- fileInput,
- uploading,
- uploadProgressList,
- handleClick,
- handleFileChange,
- };
- },
- });
- </script>
- <style scoped>
- .chunk-upload {
- padding: 20px;
- }
- .progress-item {
- margin-top: 10px;
- }
- </style>
|