百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

前端使用canvas压缩图片后再上传

toyiye 2024-05-19 19:35 16 浏览 0 评论

canvas 可以对图片进行压缩处理,原理及处理流程大致如下:

1、先将图片的 File 文件对象转成 base64 DataURL。

2、创建一个 Image 对象,src 接收文件的 base64 DataURL 后来获取图片的宽高和比例。

3、创建 canvas 画布,并依据 Image 对象的尺寸来设置画布的大小。

4、将图片绘制到 canvas 画布面。

5、使用 canvas 的 drawImage() 方法对图像进行压缩处理,并获取新的 base64 DataURL。

6、将 base64 DataURL 转换为文件对象。

7、如果服务端是接收 File 对象(文件流,二进制)来进行上传,前端可以通过 FormData.append("file", fileObject) 的方式来追加文件对象以进行上传。

将file文件对象转化为base64 DataURL

/**
 * 将文件对象转化为 base64 Data URL
 * @param {二进制文件流} file 
 * @param {回调函数,返回 base64 Data URL} callback 
 **/
readFileAsDataURL(file, callback) {
  // 如果file未定义,返回null
  if (!file) return callback(null);

  // 创建读取文件对象
  let fileReader = new FileReader();
  fileReader.readAsDataURL(file); // 读取file文件,得到的结果为base64位
  fileReader.onload = function() {
    let _this = this,
    imgBase64DataURL = _this.result; // fileReader 读取到的 base64 Data URL
    callback && callback(imgBase64DataURL);
  }
}

将base64 DataURL转换为文件对象

/**
 * 将 base64 Data URL 转换为文件对象
 * @param {baseURL} dataUrl 
 * @param {文件名称} filename 
 * @return {文件二进制流}
 */
unitedDataURLToFile(dataUrl, filename) {
  if (!dataUrl) return null;
  let arr = dataUrl.split(','),
  mime = arr[0].match(/:(.*?);/)[1],
  base64Str = atob(arr[1]),
  len = base64Str.length,
  unit8Arr = new Uint8Array(len);
  while (len--) {
    unit8Arr[len] = base64Str.charCodeAt(len);
  }
  return new File([unit8Arr], filename, {
    type: mime
  });
}

压缩方法(关键)

/**
 * 压缩方法
 * @param {参数obj} parms 
 * @param {文件二进制流} parms.file ,必传
 * @param {优化目标文件大小} parms.optimumSize ,不传初始赋值 0
 * @param {输出图片宽度} parms.width ,不传初始赋值 0,按比缩放无需传高度
 * @param {输出图片名称} parms.fileName ,不传初始赋值 image
 * @param {压缩图片程度} parms.quality ,值范围0~1,不传初始赋值 0.92。
 * @param {回调函数} parms.done ,压缩后的回调函数,压缩成功将返回 file 对象,不成功则返回错误信息
 **/
compress(parms) {
  //文件读取与图片加载等事件都是异步的,因此需要在回调函数中才能获取返回值
  if (parms && parms.done) {
    //如果file没定义,返回null
    if (parms.file == undefined) return parms.done(null);

    let _this = this;

    //给参数赋初始值
    parms.optimumSize = parms.hasOwnProperty("optimumSize") ? parms.optimumSize: 0;
    parms.width = parms.hasOwnProperty("width") ? parms.width: 100;
    parms.filename = parms.hasOwnProperty("filename") ? parms.filename: "image";
    parms.quality = parms.hasOwnProperty("quality") ? parms.quality: 0.92;

    // 得到文件类型
    let fileType = parms.file.type;
    // console.log(fileType) //image/jpeg
    if (fileType.indexOf("image") == -1) {
      console.log("请选择图片文件进行压缩理");
      return parms.done({
        code: -1,
        msg: "Only_Required_Image"
      });
    }

    // 读取 file 文件,得到的结果为 base64 字符串 Data URL
    _this.readFileAsDataURL(parms.file,
    function(base64DataURL) {
      if (base64DataURL) {
        //如果当前 fileSize 比优化目标小,直接输出
        let fileSize = parms.file.size;
        if (parms.optimumSize > fileSize) {
          console.log("无需压缩");
          //直接返回原文件信息,parms.file 是一个 JS File 对象,dataURL 是图片的 base64 DataURL
          return parms.done({
            code: 0,
            msg: "Need_Not_Compress",
            data: {
              file: parms.file,
              dataURL: base64DataURL
            }
          });
        }

        let image = new Image();
        image.src = base64DataURL;
        image.onload = function() {
          let _self = this,
          scale = _self.width / _self.height; // 获得长宽比例
          // console.log(scale);
          //创建一个canvas,并获取其上下文					
          let canvas = document.createElement('canvas'),
          context = canvas.getContext('2d');

          //获取压缩后的图片宽度,如果width为0,默认原图宽度
          canvas.width = parms.width == 0 ? _self.width: parms.width;

          //获取压缩后的图片高度,如果width为0,默认原图高度
          canvas.height = parms.width == 0 ? _self.height: parseInt(parms.width / scale);

          //把图片绘制到canvas上面
          context.drawImage(image, 0, 0, canvas.width, canvas.height);

          //压缩图片,获取到新的base64Url
          let newImageData = canvas.toDataURL(fileType, parms.quality);

          //将base64转化成文件流
          let resultFile = _this.unitedDataURLToFile(newImageData, parms.filename);

          //判断如果 optimumSize 有限制且压缩后的图片大小比优化目标大小大,就弹出错误
          if (parms.optimumSize > 0 && parms.optimumSize < resultFile.size) {
            console.log("未优化到目标压缩大小!上传图片尺寸太大,请重新选择合适图片上传");
            parms.done({
              code: -1,
              msg: "Image_Too_Large"
            });
          } else {
            //返回文件信息,resultFile 是一个 JS File 对象,dataURL 是压缩图的 base64 DataURL
            parms.done({
              code: 0,
              msg: "Complete",
              data: {
                file: resultFile,
                dataURL: newImageData
              }
            });
          }
        }
      }
    });
  }
}

当然,上述三个方法是写在一个 ES module 里的,完整的代码如下:

es module: image-compressor.js.

使用示例

新建一个示例页面,主要放置一个 <input type="file"> 控件,然后监听其 change 事件,在事件中调用压缩图片的方法。

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8" />
	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
	<meta name="renderer" content="webkit" />
	<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
	<title>前端压缩图片示例</title>
	<style>
		body {
			font-size: 16px; 
			font-family: 'PingFang SC','Microsoft YaHei','Helvetica Neue','Helvetica','Arial',sans-serif;
		}
		.form-group {
			display: flex;
			margin-bottom: .5rem;
		}
		.file-upload {
			line-height: 1.25rem;
			background-color: #fff;
			border: 1px solid #d4d4d4;
			border-radius: .125rem;
		}
		.quote {
			text-align:center; 
			color: lightgray;
		}
	</style>
</head>
<body>
	<section>
		<form name="form1">
			<div class="form-group">
				<div><label>上传图片:</label></div>
				<div>
					<input type="file" name="file" id="fileUpImg" accept="image/*" class="file-upload"/>
				</div>
			</div>
			<div class="form-group">
				<div><label style="color: transparent">图片预览:</label></div>
				<div>
					<div class="preview"><img id="compressionPreview" alt="预览图" src="./assets/img/default.jpg" width="256" /></div>
					<div class="quote"><label id="compressionTips">请选择图片上传,稍后将进行压缩</label></div>
				</div>
			</div>
		</form>	
	</section>
	
	<!--Recommended Scripts Position-->
	<script type="module">
		import {ImageCompressor} from './utils/image-compressor.js';
		var imgCompressor = new ImageCompressor(),
			fileUpImg = document.getElementById("fileUpImg"),
			compressionPreview = document.getElementById("compressionPreview"),
			compressionTips = document.getElementById("compressionTips");
		
		//监听上传控件事件
		fileUpImg.onchange = function(){			
			compressionTips.textContent = "压缩处理中,请等待完成…";
			let _this = this, fileObj = _this.files[0];
			
			//压缩图片,这里优化目标大小设为 100KB (可能优化不成功);如果优化目标大小设为0,表示只要压缩就行,不期望达到某一目标大小
			imgCompressor.compress({
				file: fileObj,
				optimumSize: 100*1024,
				width: 480,
				quality: 0.95,
				done: function(res){
					console.log(res);
					//如果压缩成功
					if(res && res.code == 0){
						//TODO
						let data = res.data;
						if(data.dataURL) {
							compressionPreview.src = data.dataURL;
							compressionTips.textContent = "压缩成功!";
						}
						
						//新建一个 FormData 对象
						let formData = new FormData();
						formData.append("file", data.file); //加入文件对象
						
						//可使用 Ajax 将 FormData 上传到服务端
						//$.ajax({
						//	url: "xxx",
						//	type: "POST",
						//	data: formData,
						//	sunccess: function(res){}
						//});
					} else {
						compressionTips.textContent = "压缩失败,未优化达到目标";
						console.log("Compress Failed. Image Too Large");
					}
				}
			});
		};		
	</script>
</body>
</html>

测试结果

测试结果截图。

本示例项目参考了网上的一些代码!本来想做一个压缩图片的递归算法,直到图片大小符合优化期望。后来发现,如果优化目标大小设置得比较小,图片如何进行压缩都无法满足条件时,会造成类似“死循环”而无法跳出,浪费资源。还有一种情况就是图片进行几次压缩之后,文件大小不会有多大改变了,有时还会增加。无法有效解决此类问题,遂放弃了递归压缩。

(完)

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码