feat: complete upload progress bar
This commit is contained in:
@@ -35,7 +35,7 @@ type PreCheckResp struct {
|
||||
func (up *uploads) PreUploadCheck(ctx fiber.Ctx, md5, ext string) (*PreCheckResp, error) {
|
||||
_, err := models.Medias.GetByHash(ctx.Context(), md5)
|
||||
if err != nil && errors.Is(err, qrm.ErrNoRows) {
|
||||
preSign, err := up.oss.PreSignUpload(ctx.Context(), fmt.Sprintf("%s%s", md5, ext))
|
||||
preSign, err := up.oss.PreSignUpload(ctx.Context(), fmt.Sprintf("%s.%s", md5, ext))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -19,8 +19,9 @@ func (c *OSSClient) GetClient() *oss.Client {
|
||||
|
||||
func (c *OSSClient) PreSignUpload(ctx context.Context, path string) (*oss.PresignResult, error) {
|
||||
request := &oss.PutObjectRequest{
|
||||
Bucket: oss.Ptr(c.config.Bucket),
|
||||
Key: oss.Ptr("quyun/" + strings.Trim(path, "/")),
|
||||
Bucket: oss.Ptr(c.config.Bucket),
|
||||
Key: oss.Ptr("quyun/" + strings.Trim(path, "/")),
|
||||
ContentType: oss.Ptr("multipart/form-data"),
|
||||
}
|
||||
log.Printf("%+v", request)
|
||||
return c.client.Presign(ctx, request)
|
||||
|
||||
@@ -11,11 +11,8 @@ export const mediaService = {
|
||||
return httpClient.post('/admin/medias', mediaInfo);
|
||||
},
|
||||
|
||||
getUploadToken() {
|
||||
return httpClient.get('/admin/uploads/token');
|
||||
},
|
||||
preUploadedCheck(md5) {
|
||||
return httpClient.get(`/admin/uploads/pre-uploaded-check/${md5}`);
|
||||
preUploadedCheck(md5, ext) {
|
||||
return httpClient.get(`/admin/uploads/pre-uploaded-check/${md5}.${ext}`);
|
||||
},
|
||||
|
||||
uploadedSuccess(data) {
|
||||
|
||||
@@ -15,7 +15,6 @@ const dropZone = ref(null);
|
||||
const uploadProgress = ref(0);
|
||||
const isUploading = ref(false);
|
||||
const currentFile = ref(null);
|
||||
const ossConfig = ref(null);
|
||||
|
||||
const uploadHistory = ref([]);
|
||||
const STORAGE_KEY = 'media_upload_history';
|
||||
@@ -56,19 +55,10 @@ const addToHistory = (file, status = 'uploading') => {
|
||||
|
||||
// Clear completed uploads
|
||||
const clearCompleted = () => {
|
||||
uploadHistory.value = uploadHistory.value.filter(item => item.status !== 'completed');
|
||||
uploadHistory.value = [];
|
||||
saveUploadHistory();
|
||||
};
|
||||
|
||||
const getOssToken = async () => {
|
||||
try {
|
||||
const response = await mediaService.getUploadToken();
|
||||
ossConfig.value = response.data;
|
||||
} catch (error) {
|
||||
toast.add({ severity: 'error', summary: '错误', detail: '获取上传凭证失败', life: 3000 });
|
||||
}
|
||||
};
|
||||
|
||||
// Add pending uploads state
|
||||
const uploadQueue = ref([]);
|
||||
const currentUploadIndex = ref(-1);
|
||||
@@ -107,10 +97,12 @@ const processNextUpload = async () => {
|
||||
|
||||
try {
|
||||
const md5Hash = await calculateMD5(nextFile.file);
|
||||
// get file ext
|
||||
const fileExt = nextFile.file.name.split('.').pop();
|
||||
// Check if file exists before upload
|
||||
const checkResult = await mediaService.preUploadedCheck(md5Hash);
|
||||
console.log(checkResult)
|
||||
if (checkResult.data === 'exists') {
|
||||
const checkResult = await mediaService.preUploadedCheck(md5Hash, fileExt);
|
||||
|
||||
if (checkResult.data.exists) {
|
||||
// Skip upload and mark as completed
|
||||
uploadQueue.value.shift();
|
||||
addToHistory(nextFile.file, 'completed');
|
||||
@@ -121,8 +113,8 @@ const processNextUpload = async () => {
|
||||
life: 3000
|
||||
});
|
||||
} else {
|
||||
// Proceed with upload
|
||||
await uploadFile(nextFile.file, md5Hash);
|
||||
// Proceed with upload using pre-signed URL
|
||||
await uploadFile(nextFile.file, md5Hash, checkResult.data.pre_sign);
|
||||
uploadQueue.value.shift();
|
||||
addToHistory(nextFile.file, 'completed');
|
||||
}
|
||||
@@ -175,34 +167,15 @@ const calculateMD5 = (file) => {
|
||||
});
|
||||
};
|
||||
|
||||
// Modify uploadFile function
|
||||
const uploadFile = async (file, md5Hash) => {
|
||||
// Modify uploadFile function to use pre-signed URL
|
||||
const uploadFile = async (file, md5Hash, preSign) => {
|
||||
try {
|
||||
currentFile.value = file;
|
||||
isUploading.value = true;
|
||||
uploadProgress.value = 0;
|
||||
|
||||
if (!ossConfig.value) {
|
||||
await getOssToken();
|
||||
}
|
||||
|
||||
const fileExt = file.name.split('.').pop();
|
||||
const newFileName = `${md5Hash}.${fileExt}`;
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append('success_action_status', '200');
|
||||
formData.append('policy', ossConfig.value.policy);
|
||||
formData.append('x-oss-signature', ossConfig.value.signature);
|
||||
formData.append('x-oss-signature-version', 'OSS4-HMAC-SHA256');
|
||||
formData.append('x-oss-credential', ossConfig.value.x_oss_credential);
|
||||
formData.append('x-oss-date', ossConfig.value.x_oss_date);
|
||||
formData.append('key', ossConfig.value.dir + newFileName);
|
||||
formData.append('x-oss-security-token', ossConfig.value.security_token);
|
||||
formData.append('callback', ossConfig.value.callback);
|
||||
formData.append('file', file);
|
||||
|
||||
xhr.upload.onprogress = (e) => {
|
||||
if (e.lengthComputable) {
|
||||
@@ -230,8 +203,16 @@ const uploadFile = async (file, md5Hash) => {
|
||||
}
|
||||
};
|
||||
|
||||
xhr.open('POST', ossConfig.value.host, true);
|
||||
xhr.send(formData);
|
||||
xhr.open(preSign.Method, preSign.URL, true);
|
||||
|
||||
// Add signed headers if provided
|
||||
if (preSign.SignedHeaders) {
|
||||
Object.entries(preSign.SignedHeaders).forEach(([key, value]) => {
|
||||
xhr.setRequestHeader(key, value);
|
||||
});
|
||||
}
|
||||
|
||||
xhr.send(file);
|
||||
});
|
||||
|
||||
toast.add({ severity: 'success', summary: '成功', detail: '文件上传成功', life: 3000 });
|
||||
@@ -346,8 +327,7 @@ onMounted(() => {
|
||||
<template #header>
|
||||
<div class="flex justify-between items-center bg-gray-50 p-4 rounded-t-lg">
|
||||
<h3 class="text-lg font-medium">上传历史</h3>
|
||||
<Button v-if="completedUploads.length" icon="pi pi-trash" text @click="clearCompleted"
|
||||
label="清除已完成" />
|
||||
<Button icon="pi pi-trash" text @click="clearCompleted" label="清除历史记录" />
|
||||
</div>
|
||||
</template>
|
||||
<template #content>
|
||||
|
||||
Reference in New Issue
Block a user