zoukankan      html  css  js  c++  java
  • JS获取剪贴板图片之后的格式选择与压缩问题

    前言

    某年某月的某一天,突然发现博客服务器上上传的图片都比较大,一些很小的截图都有几百kb,本来服务器带宽就慢,不优化一下说不过去。

    问题细述

    特别说明:本文代码因为只是用于我自己后台写markdown上传图片,运行环境只考虑PC,所以没有考虑任何兼容性,推荐Chrome下使用。

    以下面一张图片为例:

    原始图片为85kb,jpg格式的,上传之后就变成png格式了,而且变成了560kb!实在是说不过去!

    我的代码大致如下:

    // $('.editor')为markdown编辑区
    $('.editor').bind('paste', function(event)
    {
    	var e = event.originalEvent;
    	var items = e.clipboardData.items;
    	for(var i = 0; i < items.length; ++i)
    	{
    		// 如果剪贴板中的内容类型是图片文件
    		if (items[i].kind == 'file' && items[i].type.indexOf('image/') >= 0)
    		{
    			e.preventDefault();
    			var blob = items[i].getAsFile();
    			ajaxUpload(blob); // 拿到blob之后直接上传,这里省略上传代码
    		};
    	};
    });
    

    我原本以为图片原始是多大,上传之后就应该是多大的,因为是“复制粘贴”嘛,可见实际情况并非如此,问题出在哪个环节呢?

    我将相同图片采用复制粘贴的方式粘贴到word里面去,然后另存为html格式,发现保存下来的图片更大了,有将近900kb。

    虽然如此,直到现在依然不能确定,图片的转换是在当我们打开图片按下Ctrl+C放到剪贴板的时候就已经执行了,还是在js中items[i].getAsFile()中转换了,有知道的朋友欢迎指正。

    问题解决

    既然图片变大了,那就压缩嘛,压缩一般习惯在前端进行,这样可以降低上传带宽。

    处理过程:先从剪贴板获取图片,拿到的是blob对象,然后转换成base64格式的dataURL,再然后以Image形式画到canvas上,再把canvas转换成dataURL,这个转换的过程可以设置质量,最后再将dataURL转换成blob,然后上传。

    直接上代码:

    /**
     * 改变blob图片的质量,为考虑兼容性
     * @param blob 图片对象
     * @param callback 转换成功回调,接收一个新的blob对象作为参数
     * @param format 目标格式,mime格式
     * @param quality 介于0-1之间的数字,用于控制输出图片质量,仅当格式为jpg和webp时才支持质量,png时quality参数无效
     */
    function changeBlobImageQuality(blob, callback, format, quality)
    {
    	format = format || 'image/jpeg';
    	quality = quality || 0.9; // 经测试0.9最合适
    	var fr = new FileReader();
    	fr.onload = function(e)
    	{
    		var dataURL = e.target.result;
    		var img = new Image();
    		img.onload = function()
    		{
    			var canvas = document.createElement('canvas');
    			var ctx = canvas.getContext('2d');
    			canvas.width = img.width;
    			canvas.height = img.height;
    			ctx.drawImage(img, 0, 0);
    			var newDataURL = canvas.toDataURL(format, quality);
    			if(callback) callback(dataURLtoBlob(newDataURL));
    			canvas = null;
    		};
    		img.src = dataURL;
    	};
    	fr.readAsDataURL(blob); // blob 转 dataURL
    	function dataURLtoBlob(dataurl)
    	{
    		var arr = dataurl.split(','),
    			mime = arr[0].match(/:(.*?);/)[1],
    			bstr = atob(arr[1]),
    			len = bstr.length,
    			u8arr = new Uint8Array(len);
    		while (len--) u8arr[len] = bstr.charCodeAt(len);
    		return new Blob([u8arr], {type: mime});
    	}
    }
    

    修改之后的上传代码就是这样了:

    if (items[i].kind == 'file' && items[i].type.indexOf('image/') >= 0)
    {
    	e.preventDefault();
    	var blob = items[i].getAsFile();
    	changeBlobImageQuality(blob, function(newBlob)
    	{
    		ajaxUpload(newBlob); // 拿到blob之后直接上传,这里省略上传代码
    	});
    };
    

    关于dataURL转blob,有一个考虑的兼容性的代码片段如下:

    function convertDataURLToBlob(dataURL)
    {
    	var arr = dataURL.split(',');
    	var code = window.atob(arr[1]);
    	var aBuffer = new window.ArrayBuffer(code.length);
    	var uBuffer = new window.Uint8Array(aBuffer);
    	for(var i = 0; i < code.length; i++)
    	{
    		uBuffer[i] = code.charCodeAt(i);
    	}
    	var Builder = window.WebKitBlobBuilder || window.MozBlobBuilder;
    	if(Builder)
    	{
    		var builder = new Builder;
    		builder.append(buffer);
    		return builder.getBlob(format);
    	}
    	else
    	{
    		return new window.Blob([ buffer ], {type: arr[0].match(/:(.*?);/)[1]});
    	}
    }
    

    问题还没完全解决

    有一个问题,对于GIF格式的图片,复制粘贴之后只会复制第一帧(不知高手是否有解决方案)。

    还有一个问题,虽然我要解决的问题是图片上传之后变大,但是有时候本地本来就是一张压缩的比较厉害的jpg格式图片,上传之后虽然通过调整质量可以控制大小,但是有时候经过前端这么一折腾,体积虽然变化不大,但是图片质量下降的很厉害,特别是对于截图又添加文字的图片,比如下面这个:

    这是png格式效果,上传之后28kb:

    这是jpg效果,上传之后37kb,反而比png格式还要大,而且质量大打折扣:

    发现貌似是在上传一些底色为纯色的图片时,png比jpg更好,在上传传统的类似风景图片时,jpg格式更好,不知道说的有没有错。

    还有很多时候我是希望图片没有任何转换,直接原样上传,所以简单增加了个拖拽上传的功能,直接把事件绑定在body上面:

    function initDrag()
    {
    	var obj = $('body')[0];
    	obj.ondragenter = function(ev)
    	{
    		ev.dataTransfer.dropEffect = 'copy';
    	};
    	obj.ondragover = function(ev)
    	{
    		ev.preventDefault(); //防止默认事件拖入图片 放开的时候打开图片了
    		ev.dataTransfer.dropEffect = 'copy';
    	};
    	obj.ondrop = function(ev)
    	{
    		ev.preventDefault();
    		var files = ev.dataTransfer.files;
    		for(var i=0; i<files.length; i++)
    		{
    			if(files[i].type.indexOf('image') >= 0)
    			{
    				ajaxUpload(files[i]);
    			}
    		}
    	};
    }
    

    这样我就可以根据实际需要选择不同方式上传图片了:

    • 普通的白底黑字截图的话就上传png格式;
    • 普通复杂背景图片就上传jpg格式;
    • 一些gif格式还有一些本来就比较小的图片,就采用拖拽上传的方式;
  • 相关阅读:
    django QuerySet对象转换成字典对象
    HTTP请求中三种参数类型
    django开发中遇到的问题
    win7下mysql8.0.12解压缩版安装
    Django小部件
    程序员上班有什么提高效率的技巧?
    Android应用AsyncTask处理机制详解及源码分析
    Android常用工具类
    Android Volley解析
    Android 开发有哪些新技术出现?
  • 原文地址:https://www.cnblogs.com/liuxianan/p/js-image-compress.html
Copyright © 2011-2022 走看看