使用FileReader在PHP上传前批量压缩图片
嘿Marco,我刚好折腾过一模一样的需求——相机拍的图太大,移动网传不动,要在客户端压缩后再用现有PHP脚本上传。给你一套能直接嵌进你现有代码的方案,步骤很清晰:
第一步:写一个客户端图片压缩函数
核心就是用FileReader读取图片,再通过Canvas缩放绘制,最后转成压缩后的Blob/File对象。这个函数可以直接复用:
function compressImage(file, maxWidth = 1920, maxHeight = 1080, quality = 0.7) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = function(e) { const img = new Image(); img.src = e.target.result; img.onload = function() { // 计算缩放比例,保证图片不超过设定的最大尺寸 let width = img.width; let height = img.height; if (width > maxWidth || height > maxHeight) { const ratio = Math.min(maxWidth / width, maxHeight / height); width *= ratio; height *= ratio; } // 创建Canvas绘制压缩后的图片 const canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0, width, height); // 转成Blob并包装成File对象(保留原文件名) canvas.toBlob(function(blob) { const compressedFile = new File([blob], file.name, { type: 'image/jpeg' }); resolve(compressedFile); }, 'image/jpeg', quality); }; img.onerror = () => reject('图片加载失败'); }; reader.onerror = () => reject('文件读取失败'); reader.readAsDataURL(file); }); }
第二步:把压缩逻辑整合到你的现有上传流程里
你原来的JS应该有监听文件选择、预览、准备上传的代码,只需要修改文件处理部分,先压缩再走原有逻辑:
// 假设你的文件输入框选择器是你现有代码里的,这里按需调整 document.querySelectorAll('.upload-input').forEach(input => { input.addEventListener('change', async function(e) { const selectedFiles = Array.from(this.files); // 只压缩图片文件,非图片直接跳过 const compressedFiles = await Promise.all( selectedFiles.map(file => { if (!file.type.startsWith('image/')) return file; return compressImage(file); // 这里可以传自定义参数,比如compressImage(file, 1280, 720, 0.6) }) ); // ---------------------- // 这里放你原有的预览逻辑 // 比如用URL.createObjectURL(compressedFile)生成预览图地址 // ---------------------- // 准备FormData,格式和你原有上传完全一致 const formData = new FormData(); // 注意:这里的name要和你PHP后端接收的字段名完全匹配,比如你原来用的是`files[]` compressedFiles.forEach(file => { formData.append('files[]', file); }); // ---------------------- // 这里放你原有的AJAX上传代码 // 比如用fetch或者XMLHttpRequest发送formData到你的PHP脚本 // ---------------------- }); });
第三步:PHP后端几乎不用改
因为压缩后的文件还是标准的File对象,FormData的格式和你原来的上传请求完全一样,所以你现有的PHP接收代码直接能用。举个典型的例子(和你现有代码逻辑对齐):
<?php $uploadDir = 'uploads/'; if (!is_dir($uploadDir)) mkdir($uploadDir, 0755, true); $uploaded = []; if (isset($_FILES['files'])) { foreach ($_FILES['files']['tmp_name'] as $key => $tmpPath) { $fileName = basename($_FILES['files']['name'][$key]); $savePath = $uploadDir . uniqid() . '-' . $fileName; if (move_uploaded_file($tmpPath, $savePath)) { $uploaded[] = $savePath; } } } echo json_encode(['success' => true, 'files' => $uploaded]); ?>
几个小细节提醒
- 压缩参数可以按需调:
quality是0-1的数值,越小压缩率越高;maxWidth/maxHeight可以设成你能接受的最大尺寸; - 如果要处理PNG透明图,把
canvas.toBlob里的image/jpeg改成image/png就行,但PNG压缩率不如JPEG; - 记得加错误捕获:比如
Promise.all后面加.catch(err => console.error('压缩失败:', err)),避免用户上传出错没反馈; - 动态添加上传字段的话,要给新创建的input也绑定上面的
change事件监听。
内容的提问来源于stack exchange,提问作者Marco
相关产品推荐
相关产品推荐

