前言

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

问题细述

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

以下面一张图片为例:

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

我的代码大致如下:

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

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

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

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

问题解决

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

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

直接上代码:

  1. /**
  2. * 改变blob图片的质量,为考虑兼容性
  3. * @param blob 图片对象
  4. * @param callback 转换成功回调,接收一个新的blob对象作为参数
  5. * @param format 目标格式,mime格式
  6. * @param quality 介于0-1之间的数字,用于控制输出图片质量,仅当格式为jpg和webp时才支持质量,png时quality参数无效
  7. */
  8. function changeBlobImageQuality(blob, callback, format, quality)
  9. {
  10. format = format || 'image/jpeg';
  11. quality = quality || 0.9; // 经测试0.9最合适
  12. var fr = new FileReader();
  13. fr.onload = function(e)
  14. {
  15. var dataURL = e.target.result;
  16. var img = new Image();
  17. img.onload = function()
  18. {
  19. var canvas = document.createElement('canvas');
  20. var ctx = canvas.getContext('2d');
  21. canvas.width = img.width;
  22. canvas.height = img.height;
  23. ctx.drawImage(img, 0, 0);
  24. var newDataURL = canvas.toDataURL(format, quality);
  25. if(callback) callback(dataURLtoBlob(newDataURL));
  26. canvas = null;
  27. };
  28. img.src = dataURL;
  29. };
  30. fr.readAsDataURL(blob); // blob 转 dataURL
  31. function dataURLtoBlob(dataurl)
  32. {
  33. var arr = dataurl.split(','),
  34. mime = arr[0].match(/:(.*?);/)[1],
  35. bstr = atob(arr[1]),
  36. len = bstr.length,
  37. u8arr = new Uint8Array(len);
  38. while (len--) u8arr[len] = bstr.charCodeAt(len);
  39. return new Blob([u8arr], {type: mime});
  40. }
  41. }

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

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

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

  1. function convertDataURLToBlob(dataURL)
  2. {
  3. var arr = dataURL.split(',');
  4. var code = window.atob(arr[1]);
  5. var aBuffer = new window.ArrayBuffer(code.length);
  6. var uBuffer = new window.Uint8Array(aBuffer);
  7. for(var i = 0; i < code.length; i++)
  8. {
  9. uBuffer[i] = code.charCodeAt(i);
  10. }
  11. var Builder = window.WebKitBlobBuilder || window.MozBlobBuilder;
  12. if(Builder)
  13. {
  14. var builder = new Builder;
  15. builder.append(buffer);
  16. return builder.getBlob(format);
  17. }
  18. else
  19. {
  20. return new window.Blob([ buffer ], {type: arr[0].match(/:(.*?);/)[1]});
  21. }
  22. }

问题还没完全解决

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

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

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

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

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

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

  1. function initDrag()
  2. {
  3. var obj = $('body')[0];
  4. obj.ondragenter = function(ev)
  5. {
  6. ev.dataTransfer.dropEffect = 'copy';
  7. };
  8. obj.ondragover = function(ev)
  9. {
  10. ev.preventDefault(); //防止默认事件拖入图片 放开的时候打开图片了
  11. ev.dataTransfer.dropEffect = 'copy';
  12. };
  13. obj.ondrop = function(ev)
  14. {
  15. ev.preventDefault();
  16. var files = ev.dataTransfer.files;
  17. for(var i=0; i<files.length; i++)
  18. {
  19. if(files[i].type.indexOf('image') >= 0)
  20. {
  21. ajaxUpload(files[i]);
  22. }
  23. }
  24. };
  25. }

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

  • 普通的白底黑字截图的话就上传png格式;
  • 普通复杂背景图片就上传jpg格式;
  • 一些gif格式还有一些本来就比较小的图片,就采用拖拽上传的方式;

JS获取剪贴板图片之后的格式选择与压缩问题的更多相关文章

  1. 为什么不能用 JS 获取剪贴板上的内容?

    为什么不能用 JS 获取剪贴板上的内容? 为什么不能用 JS 获取剪贴板上的内容? 发一串口令给朋友朋友复制这串口令,然后访问你的网站你在网站上用 JS 读取朋友剪贴板上的口令根据不同的口令,显示不同 ...

  2. js获取当地时间并且拼接时间格式的三种方式

    js获取当地时间并且拼接时间格式,在stackoverflow上有人在问,查了资料,各种方法将时间格式改成任意自己想要的样式. 1. var date = new Date(+new Date()+8 ...

  3. js获取当前时间,并格式化为"yyyy-MM-dd HH:mm:ss"

    /** * Created by Administrator on 2019/11/15. *指尖敲打着世界 ----一个阳光而又不失帅气的少年!!!. */ // js获取当前时间,并格式化为&qu ...

  4. JS获取IMG图片高宽

    前段时间在LJW写的touchslider.js轮播代码里添加自适应屏幕大小的功能时,遇到一个问题.不管用什么样的方法都无法获取到IMG标签的高宽,最后只有给图片定一个高宽的比例值:趁今天有空我就写了 ...

  5. CEF JS实现获取剪贴板图片的DataURL

    转载:https://www.deanhan.cn/js-paste-upload.html 转载:https://segmentfault.com/a/1190000002915597 转载:htt ...

  6. js 获取div 图片高度

    使用jquery获取网页中图片的高度其实很简单,有两种常用的方法都可以打到我们的目的 $("img").whith();(返回纯数字) $("img").css ...

  7. 使用Javascript获取剪贴板图片的DataURL

    最近写博客需要插入一些截图,想着用DataURL会方便点,于是需要一个把图片转成DataURL的工具.搜索一番后发现这个功能用HTML就能实现,通过paste事件. 先尝试在Chrome上实现,Chr ...

  8. js获取当前的日期时间 格式“yyyy-MM-dd HH:MM:SS”

    function getNowFormatDate() { var date = new Date(); var seperator1 = "-"; var seperator2 ...

  9. js获取当前日期方法(YYYY-MM-DD格式)

      var myDate = new Date(); var time = myDate.toLocaleDateString().split('/').join('-');将1970/08/08转化 ...

随机推荐

  1. maven权威指南学习笔记(三)——一个简单的maven项目

    目标: 对构建生命周期 (build  lifecycle),Maven仓库 (repositories),依赖管理 (dependency management)和项目对象模型 (Project O ...

  2. centos配置虚拟主机

    首先注释掉 DocumentRoot /var/www/html 然后添加如下代码至文件底部:       NameVirtualHost 192.168.0.3     <virtualhos ...

  3. Good Bye 2016

    A - New Year and Hurry (water) #include <bits/stdc++.h> using namespace std; int main() { ]; ; ...

  4. .NET静态变量与静态方法并发的问题

    我们知道,静态变量与静态方法都是在程序编译的时候就定义好了的,并且不会存在多个副本.所以对于静态变量来说,一旦修改了就会影响全局. 因此,静态变量是存在并发性问题的,所以当我们在操作静态变量的时候需要 ...

  5. 与Android应用程序相关的文件目录都有哪些?(转载)

    与Android应用程序相关的文件目录都有哪些? | 浏览:1312 | 更新:2014-09-28 19:43 | 标签:android 一.方法介绍:   每个Android应用程序都可以通过Co ...

  6. 疑难问题解决备忘录(2)——ubuntu12.04分配swap

    分配swapdd if=/dev/zero of=Swap.disk bs=1M count=6k (count=1k创建1G的Swap,如果要创建6G则count=6k:这步比较慢) 创建swap文 ...

  7. NOI 题库 9272 题解

    9272   偶数个数字3 描述 在所有的N位数中,有多少个数中有偶数个数字3? 输入 一行给出数字N,N<=1000 输出 如题 样例输入 2 样例输出 73 Solution : 令f ( ...

  8. 新入门node.js必须要知道的概念

    一.对于一个刚入门node.js的朋友来说,一定要了解一些基础概念: 今年我正式进入社会后,发现自己所知道的IT方面的知识,真的只是牛毛,原来人外有人,山外有山,还需要继续努力.下面是一些我的自学习心 ...

  9. 不可变数组NSArray

    //数组里面不允许存放基本数据类型,只能存放“对象” NSArray *array = [NSArray arrayWithObjects:@"周星星",@"尹天仇&qu ...

  10. notepad++崩溃后文件内容变为NUL的解决方法

    毫无疑问,notepad++是一款非常优秀的文本编辑器,但是notepad++有时候会崩溃,之后文件内容就会全部变成NUL 当你打文件看到自己辛辛苦苦的成果全都变成NUL是不是有想哭的感觉?" ...