hookehuyr

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

添加 `browser-md5-file` 依赖,实现视频文件上传功能,包括文件校验、上传进度跟踪及七牛云上传逻辑。移除不再使用的 `@uppy` 相关依赖。
This diff is collapsed. Click to expand it.
......@@ -19,6 +19,7 @@
"@vant/touch-emulator": "^1.4.0",
"@vant/use": "^1.6.0",
"@videojs-player/vue": "^1.0.0",
"browser-md5-file": "^1.1.1",
"dayjs": "^1.11.13",
"swiper": "^11.2.6",
"vant": "^4.9.18",
......
......@@ -41,7 +41,7 @@
import { ref, defineProps, defineEmits, watch } from 'vue';
import { showToast } from 'vant';
import VideoPlayer from '@/components/ui/VideoPlayer.vue';
import { v4 as uuidv4 } from 'uuid';
import { uploadFile, validateFile } from '@/utils/upload';
const props = defineProps({
modelValue: {
......@@ -76,8 +76,9 @@ const uploadProgress = ref(0);
const maxSize = 100 * 1024 * 1024; // 100MB
const beforeRead = (file) => {
if (!file.type.includes('video/')) {
showToast('请上传视频文件');
const validation = validateFile(file);
if (!validation.valid) {
showToast(validation.message);
return false;
}
return true;
......@@ -90,28 +91,22 @@ const afterRead = async (file) => {
videoName.value = '';
uploadProgress.value = 0;
const formData = new FormData();
formData.append('file', file.file);
try {
// 模拟上传进度
const timer = setInterval(() => {
uploadProgress.value += 10;
if (uploadProgress.value >= 100) {
clearInterval(timer);
// 模拟上传成功后的视频URL
videoUrl.value = URL.createObjectURL(file.file);
videoId.value = uuidv4();
videoName.value = file.file.name;
}
}, 300);
// TODO: 实际的上传逻辑
// const response = await uploadVideo(formData);
// videoUrl.value = response.data.url;
// videoId.value = uuidv4();
// videoName.value = file.file.name;
// 实际上传逻辑
const fileCode = 'video'; // 视频上传的fileCode
const result = await uploadFile(file.file, fileCode, (progress) => {
uploadProgress.value = progress;
});
if (result && result.src) {
videoUrl.value = result.src;
videoId.value = result.meta_id || result.hash;
videoName.value = file.file.name;
} else {
throw new Error('上传失败');
}
} catch (error) {
uploadProgress.value = 0;
showToast('上传失败');
console.error('Upload error:', error);
}
......
import { qiniuTokenAPI, qiniuUploadAPI, saveFileAPI } from '@/api/common';
import BMF from 'browser-md5-file';
// import { v4 as uuidv4 } from 'uuid';
// 获取文件后缀
const getFileSuffix = (fileName) => {
return /.[^.]+$/.exec(fileName) || '';
};
// 获取文件MD5
const getFileMD5 = (file) => {
return new Promise((resolve, reject) => {
const bmf = new BMF();
bmf.md5(file, (err, md5) => {
if (err) {
reject(err);
return;
}
resolve(md5);
});
});
};
// 上传文件到七牛云
const uploadToQiniu = async (file, token, fileName, onProgress) => {
const formData = new FormData();
formData.append('file', file);
formData.append('token', token);
formData.append('key', fileName);
const config = {
headers: { 'Content-Type': 'multipart/form-data' },
onUploadProgress: (progressEvent) => {
if (progressEvent.total > 0) {
const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total);
// 使用requestAnimationFrame确保进度更新的平滑性
requestAnimationFrame(() => {
onProgress?.(percent);
});
}
}
};
// 根据协议选择上传地址
const qiniuUploadUrl = window.location.protocol === 'https:'
? 'https://up.qbox.me'
: 'http://upload.qiniu.com';
return await qiniuUploadAPI(qiniuUploadUrl, formData, config);
};
// 校验文件
export const validateFile = (file, options = {}) => {
const {
maxSize = 100, // 默认100MB
allowedTypes = ['video/mp4', 'video/quicktime'],
} = options;
if (!allowedTypes.includes(file.type)) {
return {
valid: false,
message: '请上传正确格式的视频文件'
};
}
const fileSize = (file.size / 1024 / 1024).toFixed(2);
if (fileSize > maxSize) {
return {
valid: false,
message: `文件大小不能超过${maxSize}MB`
};
}
return { valid: true };
};
// 上传文件
export const uploadFile = async (file, fileCode, onProgress) => {
try {
// 获取文件MD5
const md5 = await getFileMD5(file);
// 获取七牛token
const tokenResult = await qiniuTokenAPI({
name: file.name,
hash: md5
});
// 如果文件已存在,直接返回
if (tokenResult.data) {
onProgress?.(100);
return tokenResult.data;
}
// 新文件上传
if (tokenResult.token) {
const suffix = getFileSuffix(file.name);
const fileName = `uploadForm/${fileCode}/${md5}${suffix}`;
// TODO: image_info 为七牛返回的图片信息,现在是上传视频看后期适配
const { filekey, image_info } = await uploadToQiniu(
file,
tokenResult.token,
fileName,
onProgress
);
if (filekey) {
// 保存文件信息
const { data } = await saveFileAPI({
name: file.name,
filekey,
hash: md5,
height: image_info?.height,
width: image_info?.width,
});
return data;
}
}
throw new Error('上传失败');
} catch (error) {
console.error('Upload error:', error);
throw error;
}
};