hookehuyr

feat(上传): 实现视频文件上传功能并添加文件校验

添加 `browser-md5-file` 依赖,实现视频文件上传功能,包括文件校验、上传进度跟踪及七牛云上传逻辑。移除不再使用的 `@uppy` 相关依赖。
This diff is collapsed. Click to expand it.
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
19 "@vant/touch-emulator": "^1.4.0", 19 "@vant/touch-emulator": "^1.4.0",
20 "@vant/use": "^1.6.0", 20 "@vant/use": "^1.6.0",
21 "@videojs-player/vue": "^1.0.0", 21 "@videojs-player/vue": "^1.0.0",
22 + "browser-md5-file": "^1.1.1",
22 "dayjs": "^1.11.13", 23 "dayjs": "^1.11.13",
23 "swiper": "^11.2.6", 24 "swiper": "^11.2.6",
24 "vant": "^4.9.18", 25 "vant": "^4.9.18",
......
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
41 import { ref, defineProps, defineEmits, watch } from 'vue'; 41 import { ref, defineProps, defineEmits, watch } from 'vue';
42 import { showToast } from 'vant'; 42 import { showToast } from 'vant';
43 import VideoPlayer from '@/components/ui/VideoPlayer.vue'; 43 import VideoPlayer from '@/components/ui/VideoPlayer.vue';
44 -import { v4 as uuidv4 } from 'uuid'; 44 +import { uploadFile, validateFile } from '@/utils/upload';
45 45
46 const props = defineProps({ 46 const props = defineProps({
47 modelValue: { 47 modelValue: {
...@@ -76,8 +76,9 @@ const uploadProgress = ref(0); ...@@ -76,8 +76,9 @@ const uploadProgress = ref(0);
76 const maxSize = 100 * 1024 * 1024; // 100MB 76 const maxSize = 100 * 1024 * 1024; // 100MB
77 77
78 const beforeRead = (file) => { 78 const beforeRead = (file) => {
79 - if (!file.type.includes('video/')) { 79 + const validation = validateFile(file);
80 - showToast('请上传视频文件'); 80 + if (!validation.valid) {
81 + showToast(validation.message);
81 return false; 82 return false;
82 } 83 }
83 return true; 84 return true;
...@@ -90,28 +91,22 @@ const afterRead = async (file) => { ...@@ -90,28 +91,22 @@ const afterRead = async (file) => {
90 videoName.value = ''; 91 videoName.value = '';
91 uploadProgress.value = 0; 92 uploadProgress.value = 0;
92 93
93 - const formData = new FormData();
94 - formData.append('file', file.file);
95 -
96 try { 94 try {
97 - // 模拟上传进度 95 + // 实际上传逻辑
98 - const timer = setInterval(() => { 96 + const fileCode = 'video'; // 视频上传的fileCode
99 - uploadProgress.value += 10; 97 + const result = await uploadFile(file.file, fileCode, (progress) => {
100 - if (uploadProgress.value >= 100) { 98 + uploadProgress.value = progress;
101 - clearInterval(timer); 99 + });
102 - // 模拟上传成功后的视频URL 100 +
103 - videoUrl.value = URL.createObjectURL(file.file); 101 + if (result && result.src) {
104 - videoId.value = uuidv4(); 102 + videoUrl.value = result.src;
103 + videoId.value = result.meta_id || result.hash;
105 videoName.value = file.file.name; 104 videoName.value = file.file.name;
105 + } else {
106 + throw new Error('上传失败');
106 } 107 }
107 - }, 300);
108 -
109 - // TODO: 实际的上传逻辑
110 - // const response = await uploadVideo(formData);
111 - // videoUrl.value = response.data.url;
112 - // videoId.value = uuidv4();
113 - // videoName.value = file.file.name;
114 } catch (error) { 108 } catch (error) {
109 + uploadProgress.value = 0;
115 showToast('上传失败'); 110 showToast('上传失败');
116 console.error('Upload error:', error); 111 console.error('Upload error:', error);
117 } 112 }
......
1 +import { qiniuTokenAPI, qiniuUploadAPI, saveFileAPI } from '@/api/common';
2 +import BMF from 'browser-md5-file';
3 +// import { v4 as uuidv4 } from 'uuid';
4 +
5 +// 获取文件后缀
6 +const getFileSuffix = (fileName) => {
7 + return /.[^.]+$/.exec(fileName) || '';
8 +};
9 +
10 +// 获取文件MD5
11 +const getFileMD5 = (file) => {
12 + return new Promise((resolve, reject) => {
13 + const bmf = new BMF();
14 + bmf.md5(file, (err, md5) => {
15 + if (err) {
16 + reject(err);
17 + return;
18 + }
19 + resolve(md5);
20 + });
21 + });
22 +};
23 +
24 +// 上传文件到七牛云
25 +const uploadToQiniu = async (file, token, fileName, onProgress) => {
26 + const formData = new FormData();
27 + formData.append('file', file);
28 + formData.append('token', token);
29 + formData.append('key', fileName);
30 +
31 + const config = {
32 + headers: { 'Content-Type': 'multipart/form-data' },
33 + onUploadProgress: (progressEvent) => {
34 + if (progressEvent.total > 0) {
35 + const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total);
36 + // 使用requestAnimationFrame确保进度更新的平滑性
37 + requestAnimationFrame(() => {
38 + onProgress?.(percent);
39 + });
40 + }
41 + }
42 + };
43 +
44 + // 根据协议选择上传地址
45 + const qiniuUploadUrl = window.location.protocol === 'https:'
46 + ? 'https://up.qbox.me'
47 + : 'http://upload.qiniu.com';
48 +
49 + return await qiniuUploadAPI(qiniuUploadUrl, formData, config);
50 +};
51 +
52 +// 校验文件
53 +export const validateFile = (file, options = {}) => {
54 + const {
55 + maxSize = 100, // 默认100MB
56 + allowedTypes = ['video/mp4', 'video/quicktime'],
57 + } = options;
58 +
59 + if (!allowedTypes.includes(file.type)) {
60 + return {
61 + valid: false,
62 + message: '请上传正确格式的视频文件'
63 + };
64 + }
65 +
66 + const fileSize = (file.size / 1024 / 1024).toFixed(2);
67 + if (fileSize > maxSize) {
68 + return {
69 + valid: false,
70 + message: `文件大小不能超过${maxSize}MB`
71 + };
72 + }
73 +
74 + return { valid: true };
75 +};
76 +
77 +// 上传文件
78 +export const uploadFile = async (file, fileCode, onProgress) => {
79 + try {
80 + // 获取文件MD5
81 + const md5 = await getFileMD5(file);
82 +
83 + // 获取七牛token
84 + const tokenResult = await qiniuTokenAPI({
85 + name: file.name,
86 + hash: md5
87 + });
88 +
89 + // 如果文件已存在,直接返回
90 + if (tokenResult.data) {
91 + onProgress?.(100);
92 + return tokenResult.data;
93 + }
94 +
95 + // 新文件上传
96 + if (tokenResult.token) {
97 + const suffix = getFileSuffix(file.name);
98 + const fileName = `uploadForm/${fileCode}/${md5}${suffix}`;
99 +
100 + // TODO: image_info 为七牛返回的图片信息,现在是上传视频看后期适配
101 + const { filekey, image_info } = await uploadToQiniu(
102 + file,
103 + tokenResult.token,
104 + fileName,
105 + onProgress
106 + );
107 +
108 + if (filekey) {
109 + // 保存文件信息
110 + const { data } = await saveFileAPI({
111 + name: file.name,
112 + filekey,
113 + hash: md5,
114 + height: image_info?.height,
115 + width: image_info?.width,
116 + });
117 +
118 + return data;
119 + }
120 + }
121 +
122 + throw new Error('上传失败');
123 + } catch (error) {
124 + console.error('Upload error:', error);
125 + throw error;
126 + }
127 +};