Next.js截图上传表单首次提交失败,二次提交正常问题排查
解决首次提交图片压缩失败的问题
问题根源
核心问题是图片加载属于异步操作:首次提交时,设置img.src后立刻读取img.width和img.height,此时图片还未完成加载,宽高自然为0,导致画布尺寸异常,无法生成有效Blob。第二次提交时,浏览器已缓存图片资源,图片能瞬间加载完成,因此可以获取正确的宽高参数。
修复后的压缩函数
修改compressImage函数,通过监听img.onload事件,等待图片加载完成后再执行画布处理逻辑:
const compressImage = imgData => new Promise((resolve, reject) => { const img = document.createElement("img"); // 处理图片加载失败的异常情况 img.onerror = () => reject(new Error("图片加载失败")); // 图片加载完成后再执行压缩逻辑 img.onload = () => { const canvas = document.createElement("canvas"); const context = canvas.getContext("2d"); const originalWidth = img.width; const originalHeight = img.height; const baseWidth = 480; const baseHeight = 270; const canvasWidth = Math.min(baseWidth, ((originalWidth * baseHeight) / originalHeight) | 0); const canvasHeight = Math.min(baseHeight, ((originalHeight * baseWidth) / originalWidth) | 0); canvas.width = Math.min(originalWidth, canvasWidth); canvas.height = Math.min(originalHeight, canvasHeight); try { context.drawImage(img, 0, 0, canvasWidth, canvasHeight); } catch (err) { return reject(err); } // 可通过第三个参数(0-1)调整压缩质量,默认0.92 canvas.toBlob(blob => { if (blob) resolve(blob); else reject("No blob"); }, "image/jpeg", 0.8); }; img.src = imgData; // 若处理跨域图片,需添加以下配置 // img.crossOrigin = "anonymous"; });
额外优化建议
- 简化文件转URL流程:当前代码用
FileReader将文件转为dataURL,可直接用URL.createObjectURL替代,减少base64编码的额外开销,同时记得释放URL避免内存泄漏:
// 替换原FileReader逻辑 const screenshotData = URL.createObjectURL(screenshotFile); try { const compressedData = await compressImage(screenshotData); // 后续提交逻辑... } finally { // 释放URL资源 URL.revokeObjectURL(screenshotData); }
- 关于submitting状态不显示:Next.js的Form Action异步执行时,
setSubmitting(true)可能被React批量更新机制延迟,可通过await Promise.resolve()触发即时更新,不过这属于次要问题,优先解决图片压缩核心问题即可。
内容的提问来源于stack exchange,提问作者Eric H
相关产品推荐
相关产品推荐

