尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

利用es2024新特性,图片压缩及上传

利用es2024新特性,图片压缩及上传
📅 发布时间:2026/7/5 8:56:08

背景:在微信内置的浏览器,上传图片时,因考虑到手机端的图片较大,一般在5M以上,甚至更大达到10M以上;手机端的浏览器对es最新语法的支持、网速、cpu处理速度及内存大小等限制;我们需要对图片进行大小判断、图片在保证品质的前提,等比例压缩大小,然后进行上传;

大致分为三步:

1、在h5(移动端),利用h5新语法的特性来写;

2、利用es新特性,同时要兼容微信内置浏览器的内核,无论基于谷歌chrome或苹果的safiry,要兼容低版本的浏览器;

3、后端无论是python\php\java\net等,接收图片,并保存到服务器;

const MAX_SIZE = 5 * 1024 * 1024; const MAX_WIDTH = 2560; const MAX_HEIGHT = 2560; const fileInput = document.querySelector('#file'); const uploadBtn = document.querySelector('#uploadBtn'); const preview = document.querySelector('#preview'); const logEl = document.querySelector('#log'); const imageUrlInput = document.querySelector('#imageUrl'); let finalBlob = null; let finalFileName = ''; const log = (...args) => { logEl.textContent += args.join(' ') + '\n'; }; const formatSize = bytes => { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`; return `${(bytes / 1024 / 1024).toFixed(2)} MB`; }; const isJpgOrPng = file => { return ['image/jpeg', 'image/png'].includes(file.type); }; const supportWebp = async () => { const canvas = document.createElement('canvas'); canvas.width = 1; canvas.height = 1; return new Promise(resolve => { canvas.toBlob(blob => { resolve(blob?.type === 'image/webp'); }, 'image/webp', 0.8); }); }; const loadImage = file => { return new Promise((resolve, reject) => { const url = URL.createObjectURL(file); const img = new Image(); img.onload = () => { URL.revokeObjectURL(url); resolve(img); }; img.onerror = () => { URL.revokeObjectURL(url); reject(new Error('图片读取失败')); }; img.src = url; }); }; const calcTargetSize = (width, height) => { let targetWidth = width; let targetHeight = height; if (targetWidth > MAX_WIDTH || targetHeight > MAX_HEIGHT) { const ratio = Math.min(MAX_WIDTH / targetWidth, MAX_HEIGHT / targetHeight); targetWidth = Math.round(targetWidth * ratio); targetHeight = Math.round(targetHeight * ratio); } return { width: targetWidth, height: targetHeight, }; }; const canvasToBlob = (canvas, type = 'image/webp', quality = 0.82) => { return new Promise((resolve, reject) => { canvas.toBlob(blob => { if (!blob) { reject(new Error('图片压缩失败')); return; } resolve(blob); }, type, quality); }); }; const imageToCanvas = img => { const { width, height } = calcTargetSize(img.naturalWidth, img.naturalHeight); const canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; const ctx = canvas.getContext('2d', { alpha: false, desynchronized: true, }); ctx.fillStyle = '#fff'; ctx.fillRect(0, 0, width, height); ctx.drawImage(img, 0, 0, width, height); return canvas; }; const compressToWebpUnder5M = async file => { if (!isJpgOrPng(file)) { throw new Error('只允许上传 JPG 或 PNG 图片'); } const webpOk = await supportWebp(); if (!webpOk) { throw new Error('当前浏览器不支持 WebP 转换,请升级微信或系统浏览器'); } const img = await loadImage(file); let canvas = imageToCanvas(img); let quality = 0.86; let blob = await canvasToBlob(canvas, 'image/webp', quality); log('原图大小:', formatSize(file.size)); log('首次 WebP:', formatSize(blob.size)); if (blob.size <= MAX_SIZE) { return blob; } /** * 先降低质量 */ let minQuality = 0.45; let maxQuality = 0.86; for (let i = 0; i < 8; i++) { quality = (minQuality + maxQuality) / 2; const tempBlob = await canvasToBlob(canvas, 'image/webp', quality); if (tempBlob.size > MAX_SIZE) { maxQuality = quality; } else { blob = tempBlob; minQuality = quality; } } if (blob.size <= MAX_SIZE) { log('压缩后大小:', formatSize(blob.size)); return blob; } /** * 如果降质量还超过 5MB,再逐步缩小分辨率 */ let scale = 0.9; while (blob.size > MAX_SIZE && scale >= 0.4) { const oldCanvas = canvas; const newCanvas = document.createElement('canvas'); newCanvas.width = Math.round(oldCanvas.width * scale); newCanvas.height = Math.round(oldCanvas.height * scale); const ctx = newCanvas.getContext('2d', { alpha: false, desynchronized: true, }); ctx.fillStyle = '#fff'; ctx.fillRect(0, 0, newCanvas.width, newCanvas.height); ctx.drawImage(oldCanvas, 0, 0, newCanvas.width, newCanvas.height); canvas = newCanvas; blob = await canvasToBlob(canvas, 'image/webp', 0.72); log(`缩放 ${Math.round(scale * 100)}% 后:`, formatSize(blob.size)); scale -= 0.1; } if (blob.size > MAX_SIZE) { throw new Error('图片过大,压缩后仍超过 5MB,请换一张图片'); } log('最终大小:', formatSize(blob.size)); return blob; }; fileInput.addEventListener('change', async event => { logEl.textContent = ''; finalBlob = null; finalFileName = ''; const [file] = event.target.files; if (!file) { return; } try { log('正在处理图片...'); const blob = await compressToWebpUnder5M(file); finalBlob = blob; finalFileName = `${crypto.randomUUID()}.webp`; const previewUrl = URL.createObjectURL(blob); preview.src = previewUrl; preview.style.display = 'block'; log('图片已转换为 WebP'); log('待上传文件名:', finalFileName); log('待上传大小:', formatSize(blob.size)); } catch (error) { log('错误:', error.message); fileInput.value = ''; } }); uploadBtn.addEventListener('click', async () => { if (!finalBlob) { log('请先选择图片'); return; } if (finalBlob.size > MAX_SIZE) { log('图片超过 5MB,禁止上传'); return; } const formData = new FormData(); formData.append('image', finalBlob, finalFileName); uploadBtn.disabled = true; uploadBtn.textContent = '上传中...'; try { const response = await fetch('/api/index/upload/', { method: 'POST', body: formData, credentials: 'same-origin', }); const result = await response.json(); if (!response.ok || result.code !== 0) { throw new Error(result.msg || '上传失败'); } imageUrlInput.value = result.data.url; log('上传成功:', result.data.url); } catch (error) { log('上传失败:', error.message); } finally { uploadBtn.disabled = false; uploadBtn.textContent = '上传'; } });
<!doctype html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>图片上传 WebP 压缩</title> <style> body { font-family: Arial, sans-serif; padding: 16px; } #preview { max-width: 100%; margin-top: 16px; display: none; border-radius: 8px; } #log { margin-top: 16px; white-space: pre-wrap; background: #f6f6f6; padding: 12px; border-radius: 6px; font-size: 14px; } button { margin-top: 12px; padding: 10px 16px; border: 0; border-radius: 6px; background: #07c160; color: #fff; font-size: 16px; } </style> </head> <body> <h3>图片上传</h3> <input id="file" type="file" accept="image/jpeg,image/png,image/jpg"> <br> <button id="uploadBtn">上传</button> <input id="imageUrl" type="hidden" value=""> <img id="preview" alt="预览图"> <pre id="log"></pre> <script type="module" src="__API__/js/upload-webp.js"></script> </body> </html>

相关新闻

  • 2026去水印不破坏原图的方法:电脑手机在线无痕去水印工具教程
  • Navicat密码找回:3分钟解密本地加密连接配置
  • YOLO部署血泪史:PyTorch转ONNX/TensorRT/NCNN,我踩过的20个坑全在这了

最新新闻

  • 伺服系统三要素:响应、惯量与刚性解析
  • 豆包与DeepSeek本质差异:生活操作系统 vs 开发者推理引擎
  • 多旋翼无人机软着陆控制与风场估计技术详解
  • AMD Zen4移动处理器架构与能效比深度解析
  • 2026年移动与服务器处理器架构解析
  • 3分钟解锁网易云音乐:NCM转MP3的完全免费解决方案

日新闻

  • 基于YOLOv12的番茄成熟度智能检测系统开发
  • 终极RimWorld模组管理指南:用RimSort告别模组冲突烦恼
  • AI Agent框架开发:从理论到实践的完整指南

周新闻

  • 基于YOLOv12的番茄成熟度智能检测系统开发
  • 终极RimWorld模组管理指南:用RimSort告别模组冲突烦恼
  • AI Agent框架开发:从理论到实践的完整指南

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号