/** * 缩略图生成辅助函数 * 当后端 FFmpeg 生成失败时,使用浏览器原生能力作为兜底 */ /** * 使用浏览器原生 API 生成视频缩略图 * @param {File} file - 视频文件对象 * @param {number} seekTime - 截取时间点(秒) * @returns {Promise} base64 图片数据 */ export async function generateVideoThumbnailNative(file, seekTime = 0.1) { return new Promise((resolve, reject) => { const video = document.createElement('video'); const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); // 设置视频源 const url = URL.createObjectURL(file); video.src = url; video.muted = true; video.playsInline = true; video.addEventListener('loadeddata', () => { // seek 到指定时间 video.currentTime = Math.min(seekTime, video.duration * 0.1 || 0.1); }); video.addEventListener('seeked', () => { try { // 计算缩放后的尺寸(最大宽度 320) const maxWidth = 320; const scale = Math.min(1, maxWidth / video.videoWidth); canvas.width = video.videoWidth * scale; canvas.height = video.videoHeight * scale; // 绘制帧 ctx.drawImage(video, 0, 0, canvas.width, canvas.height); // 转换为 base64 const dataUrl = canvas.toDataURL('image/jpeg', 0.85); URL.revokeObjectURL(url); resolve(dataUrl); } catch (e) { URL.revokeObjectURL(url); reject(e); } }); video.addEventListener('error', (e) => { URL.revokeObjectURL(url); reject(new Error('视频加载失败')); }); // 开始加载 video.load(); }); } /** * 使用浏览器原生 API 生成图片缩略图 * @param {File} file - 图片文件对象 * @returns {Promise} base64 图片数据 */ export async function generateImageThumbnailNative(file) { return new Promise((resolve, reject) => { const img = new Image(); const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); const url = URL.createObjectURL(file); img.onload = () => { try { // 计算缩放后的尺寸(最大宽度 320) const maxWidth = 320; const scale = Math.min(1, maxWidth / img.naturalWidth); canvas.width = img.naturalWidth * scale; canvas.height = img.naturalHeight * scale; // 绘制图片 ctx.drawImage(img, 0, 0, canvas.width, canvas.height); // 转换为 base64 const dataUrl = canvas.toDataURL('image/jpeg', 0.85); URL.revokeObjectURL(url); resolve(dataUrl); } catch (e) { URL.revokeObjectURL(url); reject(e); } }; img.onerror = () => { URL.revokeObjectURL(url); reject(new Error('图片加载失败')); }; img.src = url; }); } /** * 智能缩略图生成 * 优先使用后端 FFmpeg,失败时尝试浏览器原生方案 */ export async function generateThumbnailSmart(filePath, fileType, fileObj = null) { // 首先尝试使用后端 FFmpeg(如果有 file 对象的话) if (fileObj && fileObj instanceof File) { try { if (fileType === 'video') { return await generateVideoThumbnailNative(fileObj); } else if (fileType === 'image') { return await generateImageThumbnailNative(fileObj); } } catch (e) { console.log('浏览器原生缩略图生成失败,使用后端:', e); } } // 返回 null,让后端处理 return null; }