如何从事件监听器的回调函数中返回图片压缩生成的File对象
解决异步图片压缩函数无法返回File对象的问题
这个问题的核心在于异步操作的执行顺序:FileReader的load事件是异步触发的,你的compress_image_file函数会在这个回调函数执行之前就完成执行,所以当你试图返回new_file时,它还没被创建,自然会得到undefined。
解决方案:用Promise封装异步逻辑
我们可以把整个压缩过程包装在一个Promise里,当压缩完成、new_file创建好之后,通过resolve把结果传递出去。这样调用函数时就能通过async/await或者.then()获取到最终的压缩文件。
修改后的函数代码如下:
function compress_image_file(image_file_object, quality) { // 返回一个Promise对象 return new Promise((resolve, reject) => { const file = image_file_object; const file_name = file.name.replace(/\.[^/.]+$/, ""); const reader = new FileReader(); reader.addEventListener("load", function () { try { // 声明变量,避免污染全局作用域 const data_url = reader.result; const image = new Image(); image.onload = function() { const new_width = image.width * quality / 100; const new_height = image.height * quality / 100; const canvas = document.createElement("canvas"); canvas.width = new_width; canvas.height = new_height; const ctx = canvas.getContext("2d"); ctx.drawImage(image, 0, 0, new_width, new_height); const newDataUrl = canvas.toDataURL(); const arr = newDataUrl.split(','); const mime = arr[0].match(/:(.*?);/)[1]; const ext = '.' + mime.split('/')[1]; const bstr = atob(arr[1]); const n = bstr.length; const u8arr = new Uint8Array(n); for (let i = 0; i < n; i++) { // 正向循环更清晰,避免n--的逻辑坑 u8arr[i] = bstr.charCodeAt(i); } const new_file = new File([u8arr], file_name + ext, { type: mime }); // 成功时返回压缩后的文件 resolve(new_file); } image.onerror = function(err) { // 加载图片出错时返回错误 reject(err); } image.src = data_url; } catch (err) { reject(err); } }); reader.onerror = function(err) { reject(err); } reader.readAsDataURL(file); }); }
调用方式
因为函数现在返回的是Promise,所以你需要用async/await或者.then()来获取结果:
方式1:使用async/await(推荐,代码更简洁)
async function handleImageUpload() { const file_object = input.files[0]; try { const new_file = await compress_image_file(file_object, 50); console.log(new_file); // 这里就能拿到正确的压缩后File对象了 // 在这里执行上传服务器的逻辑 } catch (err) { console.error("图片压缩失败:", err); } } // 调用这个异步函数 handleImageUpload();
方式2:使用.then()
const file_object = input.files[0]; compress_image_file(file_object, 50) .then(new_file => { console.log(new_file); // 执行上传服务器的逻辑 }) .catch(err => { console.error("图片压缩失败:", err); });
额外优化点
- 给所有变量加上
const/let声明,避免全局变量污染; - 增加了完整的错误处理(
image.onerror、reader.onerror以及try/catch),避免压缩过程中出现错误导致程序崩溃; - 补上了图片的
onload事件处理——原代码直接获取image.width可能会得到0,因为image.src设置后是异步加载的,必须等图片加载完成才能获取正确的宽高。
内容的提问来源于stack exchange,提问作者Hemendra Sahu
相关产品推荐
相关产品推荐

