This commit is contained in:
禺狨 2023-03-07 11:47:25 +08:00
commit 5ea6c8399d
3 changed files with 140 additions and 42 deletions

View File

@ -25,7 +25,6 @@ export function createResource(type: string) {
} }
export function storeResource( export function storeResource(
type: string,
categoryId: number, categoryId: number,
name: string, name: string,
extension: string, extension: string,
@ -33,9 +32,12 @@ export function storeResource(
disk: string, disk: string,
fileId: string, fileId: string,
path: string, path: string,
url: string url: string,
extra: object
) { ) {
return client.post("/backend/v1/resource/create", { let data = Object.assign(
{},
{
category_id: categoryId, category_id: categoryId,
name, name,
extension, extension,
@ -44,7 +46,10 @@ export function storeResource(
file_id: fileId, file_id: fileId,
path, path,
url, url,
}); },
extra
);
return client.post("/backend/v1/resource/create", data);
} }
export function destroyResource(id: number) { export function destroyResource(id: number) {

View File

@ -7,13 +7,15 @@ import {
Progress, Progress,
Row, Row,
Table, Table,
Tag,
Upload, Upload,
} from "antd"; } from "antd";
import Dragger from "antd/es/upload/Dragger"; import Dragger from "antd/es/upload/Dragger";
import { useEffect, useState } from "react"; import { useRef, useState } from "react";
import { generateUUID, getToken } from "../../utils"; import { generateUUID } from "../../utils";
import { minioUploadId } from "../../api/upload"; import { minioUploadId } from "../../api/upload";
import { UploadChunk } from "../../js/minio-upload-chunk"; import { UploadChunk } from "../../js/minio-upload-chunk";
import { storeResource } from "../../api/resource";
interface PropsInterface { interface PropsInterface {
categoryId: number; categoryId: number;
@ -27,13 +29,18 @@ interface FileItem {
size: number; size: number;
progress: number; progress: number;
file: File; file: File;
resource_type: string; resourceType: string;
loading: boolean; loading: boolean;
run: UploadChunk; run: UploadChunk;
isSuc: boolean;
isErr: boolean;
errMsg: string;
remoteName: string;
} }
export const UploadVideoButton = (props: PropsInterface) => { export const UploadVideoButton = (props: PropsInterface) => {
const [showModal, setShowModal] = useState(false); const [showModal, setShowModal] = useState(false);
const localFileList = useRef<FileItem[]>([]);
const [fileList, setFileList] = useState<FileItem[]>([]); const [fileList, setFileList] = useState<FileItem[]>([]);
const getMinioUploadId = async () => { const getMinioUploadId = async () => {
@ -47,6 +54,7 @@ export const UploadVideoButton = (props: PropsInterface) => {
if (file.type === "video/mp4") { if (file.type === "video/mp4") {
//添加到本地待上传 //添加到本地待上传
let data = await getMinioUploadId(); let data = await getMinioUploadId();
let run = new UploadChunk(file, data["upload_id"], data["filename"]);
let item: FileItem = { let item: FileItem = {
id: generateUUID(), id: generateUUID(),
duration: 0, duration: 0,
@ -54,16 +62,54 @@ export const UploadVideoButton = (props: PropsInterface) => {
size: file.size, size: file.size,
progress: 0, progress: 0,
file: file, file: file,
resource_type: data["resource_type"], resourceType: data["resource_type"],
loading: true, loading: true,
run: new UploadChunk( run: run,
file, isSuc: false,
data["upload_id"], isErr: false,
data["filename"] errMsg: "",
), remoteName: data["filename"],
}; };
item.run.on("success", (url: string) => {
item.isSuc = true;
setFileList([...localFileList.current]);
// 创建上传记录
storeResource(
props.categoryId,
item.file.name,
"mp4",
item.file.size,
"minio",
"",
item.remoteName,
url,
{
duration: item.duration,
}
).then(() => {
message.success(`${item.file.name} 上传成功`);
});
});
item.run.on("retry", () => {
item.isErr = false;
item.errMsg = "";
setFileList([...localFileList.current]);
});
item.run.on("progress", (progress: number) => {
item.progress = progress;
setFileList([...localFileList.current]);
});
item.run.on("error", (msg: string) => {
item.isErr = true;
item.errMsg = msg;
setFileList([...localFileList.current]);
});
setTimeout(() => {
item.run.start(); item.run.start();
setFileList([item, ...fileList]); }, 500);
localFileList.current.push(item);
setFileList([...localFileList.current]);
} else { } else {
message.error(`${file.name} 并不是 mp4 视频文件`); message.error(`${file.name} 并不是 mp4 视频文件`);
} }
@ -103,6 +149,13 @@ export const UploadVideoButton = (props: PropsInterface) => {
open={true} open={true}
onCancel={() => { onCancel={() => {
setShowModal(false); setShowModal(false);
props.onUpdate();
}}
maskClosable={false}
closable={false}
onOk={() => {
setShowModal(false);
props.onUpdate();
}} }}
> >
<Row gutter={[0, 10]}> <Row gutter={[0, 10]}>
@ -145,7 +198,7 @@ export const UploadVideoButton = (props: PropsInterface) => {
{record.progress > 0 && ( {record.progress > 0 && (
<Progress <Progress
size="small" size="small"
steps={10} steps={20}
percent={record.progress} percent={record.progress}
/> />
)} )}
@ -155,17 +208,34 @@ export const UploadVideoButton = (props: PropsInterface) => {
{ {
title: "操作", title: "操作",
key: "action", key: "action",
render: (index, record) => ( render: (_, record) => (
<> <>
{record.progress === 0 && ( {record.progress > 0 &&
record.isSuc === false &&
record.isErr === false && (
<Button <Button
type="link"
onClick={() => { onClick={() => {
fileList.splice(index, 1); record.run.cancel();
}} }}
> >
</Button> </Button>
)} )}
{record.isErr && (
<>
<Tag color="red">{record.errMsg}</Tag>
<Button
type="link"
onClick={() => {
record.run.retry();
}}
>
</Button>
</>
)}
</> </>
), ),
}, },

View File

@ -13,7 +13,8 @@ export class UploadChunk {
filename: string; filename: string;
onError: ((err: string) => void | undefined) | undefined; onError: ((err: string) => void | undefined) | undefined;
onSuccess: (() => void | undefined) | undefined; onSuccess: ((url: string) => void | undefined) | undefined;
onRetry: (() => void | undefined) | undefined;
onProgress: ((progress: number) => void) | undefined; onProgress: ((progress: number) => void) | undefined;
constructor(file: File, uploadId: string, filename: string) { constructor(file: File, uploadId: string, filename: string) {
@ -32,22 +33,41 @@ export class UploadChunk {
this.filename = filename; this.filename = filename;
} }
on(event: string, handle: any) {
if (event === "error") {
this.onError = handle;
} else if (event === "success") {
this.onSuccess = handle;
} else if (event === "progress") {
this.onProgress = handle;
} else if (event === "retry") {
this.onRetry = handle;
}
}
start() { start() {
if (this.isStop) { if (this.isStop) {
return; return;
} }
let start = (this.chunkIndex - 1) * this.chunkSize; if (this.chunkIndex > this.chunkNumber) {
if (start > this.file.size) {
//上传完成 //上传完成
minioMerge(this.filename, this.uploadId) minioMerge(this.filename, this.uploadId)
.then((res) => { .then((res: any) => {
console.log("合并成功", res); let url = res.data.url;
this.onSuccess && this.onSuccess(url);
}) })
.catch((e) => { .catch((e) => {
console.error("合并失败", e); console.error("文件合并失败", e);
this.onError && this.onError("失败.3");
}); });
return; return;
} }
this.onProgress &&
this.onProgress(
parseInt((this.chunkIndex / this.chunkNumber) * 100 + "")
);
let start = (this.chunkIndex - 1) * this.chunkSize;
const chunkData = this.file.slice(start, start + this.chunkSize); const chunkData = this.file.slice(start, start + this.chunkSize);
const boolname = this.file.name + "-" + this.chunkIndex; const boolname = this.file.name + "-" + this.chunkIndex;
const tmpFile = new File([chunkData], boolname); const tmpFile = new File([chunkData], boolname);
@ -65,16 +85,19 @@ export class UploadChunk {
this.start(); this.start();
}) })
.catch((e) => { .catch((e) => {
console.error("获取签名url失败", e); console.error("文件分片上传失败", e);
this.onError && this.onError("失败.2");
}); });
} }
stop() { cancel() {
this.isStop = true; this.isStop = true;
this.onError && this.onError("已取消");
} }
resume() { retry() {
this.isStop = false; this.isStop = false;
this.start(); this.start();
this.onRetry && this.onRetry();
} }
} }