From 709027df50856d805399fc55776f27a530d81c67 Mon Sep 17 00:00:00 2001 From: none Date: Mon, 6 Mar 2023 18:22:04 +0800 Subject: [PATCH] =?UTF-8?q?minio=E5=88=86=E7=89=87=E4=B8=8A=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/upload.ts | 22 +++++- src/compenents/upload-video-button/index.tsx | 55 +++----------- src/js/minio-upload-chunk.ts | 80 ++++++++++++++++++++ 3 files changed, 112 insertions(+), 45 deletions(-) create mode 100644 src/js/minio-upload-chunk.ts diff --git a/src/api/upload.ts b/src/api/upload.ts index 28341ec..2d8af35 100644 --- a/src/api/upload.ts +++ b/src/api/upload.ts @@ -7,8 +7,26 @@ export function image(categoryId: number, file: File) { }); } -export function minioToken(extension: string) { - return client.get("/backend/v1/upload/minio-token", { +export function minioUploadId(extension: string) { + return client.get("/backend/v1/upload/minio-upload-id", { extension, }); } +export function minioPreSignUrl( + uploadId: string, + filename: string, + partNumber: number +) { + return client.get("/backend/v1/upload/minio-pre-sign-url", { + upload_id: uploadId, + filename, + part_number: partNumber, + }); +} + +export function minioMerge(filename: string, uploadId: string) { + return client.get("/backend/v1/upload/minio-merge", { + filename, + upload_id: uploadId, + }); +} diff --git a/src/compenents/upload-video-button/index.tsx b/src/compenents/upload-video-button/index.tsx index fbe0761..8f04153 100644 --- a/src/compenents/upload-video-button/index.tsx +++ b/src/compenents/upload-video-button/index.tsx @@ -12,8 +12,8 @@ import { import Dragger from "antd/es/upload/Dragger"; import { useEffect, useState } from "react"; import { generateUUID, getToken } from "../../utils"; -import { minioToken } from "../../api/upload"; -import Resumable from "resumablejs"; +import { minioUploadId } from "../../api/upload"; +import { UploadChunk } from "../../js/minio-upload-chunk"; interface PropsInterface { categoryId: number; @@ -29,30 +29,24 @@ interface FileItem { file: File; resource_type: string; loading: boolean; - R: Resumable | undefined; + run: UploadChunk; } export const UploadVideoButton = (props: PropsInterface) => { const [showModal, setShowModal] = useState(false); const [fileList, setFileList] = useState([]); - const getMinioConfig = async () => { - let resp: any = await minioToken("mp4"); + const getMinioUploadId = async () => { + let resp: any = await minioUploadId("mp4"); return resp.data; }; - const r = new Resumable({ - chunkSize: 1 * 1024 * 1024, - fileParameterName: "file", - uploadMethod: "POST", - }); - const uploadProps = { multiple: true, beforeUpload: async (file: File) => { if (file.type === "video/mp4") { //添加到本地待上传 - let data = await getMinioConfig(); + let data = await getMinioUploadId(); let item: FileItem = { id: generateUUID(), duration: 0, @@ -62,38 +56,13 @@ export const UploadVideoButton = (props: PropsInterface) => { file: file, resource_type: data["resource_type"], loading: true, - R: undefined, + run: new UploadChunk( + file, + data["upload_id"], + data["filename"] + ), }; - - // 初始化上传对象 - let r = new Resumable({ - target: data["url"], - chunkSize: 6 * 1024 * 1024, - simultaneousUploads: 1, - uploadMethod: "PUT", - method: "octet", - testChunks: false, //不校验已上传的chunks - chunkNumberParameterName: "partNumber", - query: { - uploadId: item.id, - }, - }); - r.on("fileProgress", (file: Resumable.ResumableFile) => { - item.progress = file.progress(false) * 100; - console.log("进度", item.progress); - }); - r.on("error", (e) => { - console.log("错误", e); - }); - r.addFile(item.file); - - item.R = r; - setTimeout(() => { - if (item.R) { - item.R.upload(); - } - }, 500); - + item.run.start(); setFileList([item, ...fileList]); } else { message.error(`${file.name} 并不是 mp4 视频文件`); diff --git a/src/js/minio-upload-chunk.ts b/src/js/minio-upload-chunk.ts new file mode 100644 index 0000000..5293755 --- /dev/null +++ b/src/js/minio-upload-chunk.ts @@ -0,0 +1,80 @@ +import axios, { Axios } from "axios"; +import { minioMerge, minioPreSignUrl } from "../api/upload"; + +export class UploadChunk { + client: Axios; + file: File; + progress: number; + chunkNumber: number; + isStop: boolean; + chunkSize: number; + chunkIndex: number; + uploadId: string; + filename: string; + + onError: ((err: string) => void | undefined) | undefined; + onSuccess: (() => void | undefined) | undefined; + onProgress: ((progress: number) => void) | undefined; + + constructor(file: File, uploadId: string, filename: string) { + this.client = axios.create({ + timeout: 15000, + withCredentials: false, + }); + this.file = file; + this.progress = 0; + this.isStop = false; + this.chunkIndex = 1; + this.chunkSize = 6 * 1024 * 1024; + this.chunkNumber = Math.ceil(file.size / this.chunkSize); + + this.uploadId = uploadId; + this.filename = filename; + } + + start() { + if (this.isStop) { + return; + } + let start = (this.chunkIndex - 1) * this.chunkSize; + if (start > this.file.size) { + //上传完成 + minioMerge(this.filename, this.uploadId) + .then((res) => { + console.log("合并成功", res); + }) + .catch((e) => { + console.error("合并失败", e); + }); + return; + } + const chunkData = this.file.slice(start, start + this.chunkSize); + const boolname = this.file.name + "-" + this.chunkIndex; + const tmpFile = new File([chunkData], boolname); + + minioPreSignUrl(this.uploadId, this.filename, this.chunkIndex) + .then((res: any) => { + return this.client.put(res.data.url, tmpFile, { + headers: { + "Content-Type": "multipart/form-data", + }, + }); + }) + .then(() => { + this.chunkIndex += 1; + this.start(); + }) + .catch((e) => { + console.error("获取签名url失败", e); + }); + } + + stop() { + this.isStop = true; + } + + resume() { + this.isStop = false; + this.start(); + } +}