最近在使用Semi Design的Upload组件时遇到一个典型问题:当实现自定义压缩功能后,上传文件的操作无法正常触发onChange事件。这个现象在需要前端预处理文件的场景中尤为常见,特别是图片上传前的压缩优化场景。
具体表现为:
Semi Design的Upload组件内部采用Promise链式处理上传流程。当自定义beforeUpload返回Promise时,组件会等待该Promise resolved才会继续后续流程。常见的问题根源在于:
javascript复制const beforeUpload = (file) => {
// 错误示例1:直接修改原文件对象
compressImage(file).then(compressed => {
file = compressed // 这会导致引用丢失
return true
})
// 错误示例2:未返回Promise链
compressImage(file)
return true
}
javascript复制const beforeUpload = async (file) => {
try {
const compressedFile = await compressImage(file)
return compressedFile // 关键点1:返回新文件对象
} catch (error) {
console.error('压缩失败:', error)
return false // 关键点2:异常处理
}
}
// 压缩工具函数示例
const compressImage = (file) => {
return new Promise((resolve) => {
const reader = new FileReader()
reader.onload = (event) => {
const img = new Image()
img.src = event.target.result
img.onload = () => {
const canvas = document.createElement('canvas')
// ...实现压缩逻辑...
canvas.toBlob((blob) => {
resolve(
new File([blob], file.name, {
type: 'image/jpeg',
lastModified: Date.now()
})
)
}, 'image/jpeg', 0.6)
}
}
reader.readAsDataURL(file)
})
}
保持文件引用一致:
Promise链完整性:
组件状态更新:
javascript复制// 在Upload组件上添加调试属性
<Upload
debug
beforeUpload={beforeUpload}
onChange={(file, fileList, event) => {
console.log('事件流追踪:', { file, fileList, event })
}}
/>
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| onChange完全不触发 | beforeUpload未返回Promise | 确保返回Promise或boolean |
| 文件状态卡在uploading | Promise未resolve/reject | 检查压缩逻辑是否有未处理的异常 |
| 控制台报类型错误 | 返回了非File对象 | 使用new File()构造函数 |
| 多次选择相同文件无效 | lastModified未更新 | 在new File()中设置新时间戳 |
javascript复制// 对大文件分片处理
const CHUNK_SIZE = 1024 * 1024 // 1MB
if (file.size > 5 * CHUNK_SIZE) {
return compressInChunks(file)
}
javascript复制// 将压缩逻辑移至Worker线程
const worker = new Worker('./compress.worker.js')
worker.postMessage({ file })
worker.onmessage = (e) => {
resolve(e.data.compressed)
}
javascript复制// 及时释放资源
URL.revokeObjectURL(img.src)
canvas.width = 0
canvas.height = 0
javascript复制const beforeUpload = async (fileList) => {
return Promise.all(
fileList.map(file =>
compressImage(file).catch(() => file) // 压缩失败保留原文件
)
)
}
javascript复制// 根据文件类型动态调整质量
const getQuality = (type) => {
return type.includes('png') ? 0.8 : 0.6
}
canvas.toBlob(blob => {
resolve(new File([blob], file.name, {
type: file.type,
lastModified: Date.now()
}))
}, file.type, getQuality(file.type))
Semi Design 版本差异:
浏览器兼容处理:
javascript复制// 处理Safari的Blob兼容问题
const safeBlob = blob =>
blob || new Blob([data], { type: 'image/jpeg' })
在实际项目中遇到的典型情况是,当压缩后的文件体积反而大于原始文件时(常见于某些PNG图片),建议添加体积校验逻辑:
javascript复制const shouldCompress = (original, compressed) => {
return compressed.size < original.size * 0.9
}