base64和blob
base64是二进制数据的一个编码格式,就像utf8一样的东西,他跟json一样,也是前后端交互能够相互识别的数据,他更多的是用来传递文件数据,并且如果是图片的base64,可以用来压缩
获取base64有几个方式
- 用input + filereader
- 用url + canvas
- 用url + filereader
在js里生成base64的API有两个,一个是FileReader,一个是画布canvas
input + filereader
<input type="file" onchange="change(this.files[0])">
function change(file){
   var fr = new FileReader()
   fr.onload = function(e) {
      // 这个就是base64
      console.log( e.target.result );
   }
   // 这个方法传参是一个Blob类型的格式
   fr.readAsDataURL(file)
}
url + canvas
我们使用画布是为了获取画布上的内容
画布的输入是图片,然后对这个图片进行剪切,打水印什么的
画布上的内容的输出格式是base64
// 这个image就是输入
// 除了new,也可以直接取页面上的标签
var image = new Image();
image.onload = function () {
   var w = image.width;
   var h = image.height;
   var canvas = document.createElement('canvas');
   var ctx = canvas.getContext("2d");
   canvas.width = w;
   canvas.height = h;
   ctx.drawImage(image, 0, 0, w, h);
   // 可以在这里添加水印或者合并图片什么的
   ...
   // 把画布的内容转成base64,这个就是输出
   var base64 = canvas.toDataURL('image/jpeg');
   console.log(base64)
}
// 这个src可以是本地路径,服务器图片地址,也可以是上面fileReader的base64
image.src = "xxx.jpg";
url + filereader
解决方案是不通过画布获得base64,可以通过请求图片地址,修改响应头获得blob 格式文件,然后让fileReader把blob转成base64,缺点是请求不允许跨域,上代码
function getBase64(imgUrl) {
   window.URL = window.URL || window.webkitURL;
   var xhr = new XMLHttpRequest();
   xhr.open("get", imgUrl, true);
   // 至关重要
   xhr.responseType = "blob";
   xhr.onload = function () {
	if (this.status == 200) {
	     //得到一个blob对象
	     var blob = this.response;
	     console.log("blob", blob)
	     //  把blob转成base64
	     let fr = new FileReader();
	     fr.onloadend = function (e) {
		  let base64 = e.target.result;
		  console.log(base64)
	     };
	     fr.readAsDataURL(blob);
             // 补充知识,把blob转成内存地址
	     var img = document.createElement("img");
	     img.onload = function (e) {
		  window.URL.revokeObjectURL(img.src); // 清除释放
	     };
             // 把blob转化成当前页面的一个内存地址
	     let src = window.URL.createObjectURL(blob); // 这个方法也可以传一个file
	     console.log(src)
	     img.src = src;
	     document.getElementById("xxx").appendChild(img);
	}
  }
  xhr.send();
}
getBase64("http://www.xx.com/xx.png")
关于blob
上面使用到了blob的知识
从input的onchange中返回的图片对象其实就是一个File对象。
而Blob对象是一个用来包装二进制文件的容器,File继承于Blob。
FileReader是用来读取内存中的文件的API,支持File和Blob两种格式。
FileReader和URL.createObjectURL的区别
资料来自掘金网友
区别一
- 通过FileReader.readAsDataURL(file)可以获取一段data:base64的字符串
- 通过URL.createObjectURL(blob)可以获取当前文件的一个内存URL
区别二
- createObjectURL是同步执行(立即的)
- FileReader.readAsDataURL是异步执行(过一段时间)
区别三
- createObjectURL返回一段带hash的url,并且一直存储在内存中,直到document触发了unload事件(例如:document close)或者执行revokeObjectURL来释放。
- FileReader.readAsDataURL则返回包含很多字符的base64,并会比blob url消耗更多内存,但是在不用的时候会自动从内存中清除(通过垃圾回收机制)
压缩图片
三个获取base64的方法说完了,但是base64的初衷是加工图片或者压缩图片,否则我们获取base64将毫无意义,加工图片有擦除,打水印,剪切等等,这个可以查看下一篇笔记,这里先讲压缩,压缩是使用canvas的API,但是有很多的兼容和限制
// 基础使用
var bili = 0.7; // 压缩比例
var base64 = canvas.toDataURL('image/jpeg',bili);
画布的兼容和限制
画布的api是非常不友好的
- 跨域问题(往下看有解决方案)
- ios系统兼容(所以有些需求要做手机网页版的ps的千万别接)
- 注意几个细节
- 拍照还会旋转(往下看有解决方案)
关于画布跨域
张大神的跨域的解决方案
// 不管画布的输入,即图片的来源是new Image() 还是 document.querySelector()
// 如果图片是外链,产生了跨域,那画布会报错
// Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
// Tainted canvases 【被污染的画布】
// 解决方案是先让后端开启图片支持跨域CORS
// 然后在new Image() 或者 document.querySelector() 之后加上
img.crossOrigin=""
ios系统兼容
图片压缩上传实践
在IOS中,canvas绘制图片是有两个限制:
- 如果图片的大小超过两百万像素,图片也是无法绘制到canvas上的,调用drawImage的时候不会报错,但是你用toDataURL获取图片数据的时候获取到的是空的图片数据,在安卓或者PC浏览器就只要比画布小就行
- canvas的大小有限制,如果canvas的大小大于大概五百万像素(即宽高乘积)的时候,画布是全黑的一片,而安卓和PC浏览器会大得多,测试的PChorme浏览器是16000*16000
var image = new Image();
// 在画布大于 16000*16000 之后就画不出来,这是谷歌78.0.3904.97
// 几年前的资料显示ios的画布是 400w
var w = 16000;
image.onload = function () {
   var canvas = document.querySelector('#canvas');
   var ctx = canvas.getContext("2d");
   canvas.width = w;
   canvas.height = w;
   ctx.drawImage(image, 0, 0, w, w);
}
image.src = "./img/aa.jpg";
应对措施
- 第一种限制,处理办法就是瓦片绘制了,也就是将图片分割成多块绘制到canvas上,我代码里的做法是把图片分割成100万像素一块的大小,再绘制到canvas上。
- 第二种限制,我的处理办法是对图片的宽高进行适当压缩,我代码里为了保险起见,设的上限是四百万像素,如果图片大于四百万像素就压缩到小于四百万像素。四百万像素的图片应该够了,算起来宽高都有2000X2000了
// 用于压缩图片的canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext('2d');
//  瓦片canvas
var tCanvas = document.createElement("canvas");
var tctx = tCanvas.getContext("2d");
// 把base64放到img里,再把图片传到方法里
function compress(img) {
    var initSize = img.src.length;  //base64的长度
    var width = img.width;
    var height = img.height;
    //如果图片大于四百万像素,计算压缩比并将大小压至400万以下
    var ratio;
    if ((ratio = width * height / 4000000) > 1) {
      ratio = Math.sqrt(ratio);
      width /= ratio;
      height /= ratio;
    } else {
      ratio = 1;
    }
    canvas.width = width;
    canvas.height = height;
    //铺底色,细节之一
    ctx.fillStyle = "#fff";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    //如果图片像素大于100万则使用瓦片绘制
    var count;
    if ((count = width * height / 1000000) > 1) {
      count = ~~(Math.sqrt(count) + 1); //计算要分成多少块瓦片
      //计算每块瓦片的宽和高
      var nw = ~~(width / count);
      var nh = ~~(height / count);
      tCanvas.width = nw;
      tCanvas.height = nh;
      for (var i = 0; i < count; i++) {
        for (var j = 0; j < count; j++) {
          tctx.drawImage(img, i * nw * ratio, j * nh * ratio, nw * ratio, nh * ratio, 0, 0, nw, nh);
          ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh);
        }
      }
    } else {
      ctx.drawImage(img, 0, 0, width, height);
    }
    //进行最小压缩
    var ndata = canvas.toDataURL('image/jpeg', 0.1);
    console.log('压缩前:' + initSize);
    console.log('压缩后:' + ndata.length);
    console.log('压缩率:' + ~~(100 * (initSize - ndata.length) / initSize) + "%");
    tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0;
    return ndata;
}
注意几个细节
- 一个就是canvas的toDataURL是只能压缩jpg的,当用户上传的图片是png的话,就需要转成jpg,也就是统一用canvas.toDataURL('image/jpeg') , 类型统一设成jpeg,而压缩比就自己控制了
- 另一个就是如果是png转jpg,绘制到canvas上的时候,canvas存在透明区域的话,当转成jpg的时候透明区域会变成黑色,因为canvas的透明像素默认为rgba(0,0,0,0),所以转成jpg就变成rgba(0,0,0,1)了,也就是透明背景会变成了黑色。解决办法就是绘制之前在canvas上铺一层白色的底色
拍照旋转的原因和解决方案
资料来自掘金网友
为什么从相机拍照获取的图片会旋转呢?
是因为从相机拍照获取的图片的EXIF(Exchangeable image file format)会默认设置一个orientation tag
目前只有jpeg格式的图片会有

解决方案有
用EXIF.js插件获取图片的orientation进行判断
简易版EXIF.js用DataViewAPI  代码来自stackoverflow
function getOrientation(file, callback) {
    var reader = new window.FileReader();
    reader.onload = function (e) {
        var view = new window.DataView(e.target.result);
        if (view.getUint16(0, false) != 0xFFD8) {
            return callback(-2);
        }
        var length = view.byteLength, offset = 2;
        while (offset < length) {
            var marker = view.getUint16(offset, false);
            offset += 2;
            if (marker == 0xFFE1) {
                if (view.getUint32(offset += 2, false) != 0x45786966) {
                    return callback(-1);
                }
                var little = view.getUint16(offset += 6, false) == 0x4949;
                offset += view.getUint32(offset + 4, little);
                var tags = view.getUint16(offset, little);
                offset += 2;
                for (var i = 0; i < tags; i++) {
                    if (view.getUint16(offset + (i * 12), little) == 0x0112) {
                        return callback(view.getUint16(offset + (i * 12) + 8, little));
                    }
                }
            } else if ((marker & 0xFF00) != 0xFF00) {
                break;
            } else {
                offset += view.getUint16(offset, false);
            }
        }
        return callback(-1);
    };
    reader.readAsArrayBuffer(file);
}
// 将图片旋转到正确的角度
function resetOrientation(srcBase64, srcOrientation, callback) {
    var img = new Image();
    img.onload = function() {
        var width = img.width,
            height = img.height,
            canvas = document.createElement('canvas'),
            ctx = canvas.getContext("2d");
        // set proper canvas dimensions before transform & export
        if ([5,6,7,8].indexOf(srcOrientation) > -1) {
            canvas.width = height;
            canvas.height = width;
        } else {
            canvas.width = width;
            canvas.height = height;
        }
        // transform context before drawing image
        // -2: not jpeg
        // -1: not defined
        switch (srcOrientation) {
            case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;
            case 3: ctx.transform(-1, 0, 0, -1, width, height ); break;
            case 4: ctx.transform(1, 0, 0, -1, 0, height ); break;
            case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
            case 6: ctx.transform(0, 1, -1, 0, height , 0); break;
            case 7: ctx.transform(0, -1, -1, 0, height , width); break;
            case 8: ctx.transform(0, -1, 1, 0, 0, width); break;
            default: ctx.transform(1, 0, 0, 1, 0, 0);
        }
        // draw image
        ctx.drawImage(img, 0, 0);
        // export base64,然后用画布旋转正确后再生成正确的base64
        callback(canvas.toDataURL('image/jpeg'));
    };
    img.src = srcBase64;
};
如果有关于手机画布的需要能不做尽量不做,pc端可以做一做,微信的相册自带的画布是手机端最好使的,微信把手机的兼容都大概搞定了,这也是为什么说再别人的平台上做网页好的地方,API众多,兼容性也很好,微信的代码查看《微信公众号》篇
base64和blob的更多相关文章
- JS实现图片base64转blob对象,压缩图片,预览图片,图片旋转到正确角度
		base64转blob对象 /** 将base64转换为文件对象 * @param {String} base64 base64字符串 * */ var convertBase64ToBlob = f ... 
- Base64转Blob
		最近碰见一个问题,a链接的href为base64,但在chrome下载时显示下载失败,经查询,base64过大会在某些浏览器上下载失败(如新版chrome),解决方法是将base64转为blob fu ... 
- base64 和 Blob 相互转换
		Base64 to Blob function dataURLtoBlob(dataurl) { var arr = dataurl.split(','), mime = arr[0].match(/ ... 
- base64和Blob互相转换
		1.base64转blob(二进制数据) /** * 将以base64的图片url数据转换为Blob * @param urlData 用url方式表示的base64图片数据 */ functio ... 
- 图片预览(base64和blob:图片链接)和ajax上传、下载(带进度提示)
		直接上代码 html和js <!DOCTYPE html> <html> <head> <meta name="viewport" con ... 
- uniapp中拿到base64转blob对象,或base64转bytes字节数组,io操作写入字节流文件bytes
		1. uniAPP中拿到附件的base64如何操作,如word文件 /*** 实现思路:* 通过native.js的io操作创建文件,拿到平台绝对路径* 再通过原生类进行base64解码,拿到字节流b ... 
- 关于web前端base64转换为Blob,存入数组后 ajax请求传输到后端 接受不到文件问题
		前端console输出是正常Blob对象,通过ajax formdata 传输到 后端java SpringMvc用MultipartFile接受却一直接受不到,后来直接解析HttpServletRe ... 
- JS 实现blob与base64互转
		/** * base64 to blob二进制 */ function dataURItoBlob(dataURI) { var mimeString = dataURI.split(',')[0]. ... 
- js实现图片的Blob  base64  ArrayBuffer 的各种转换
		一.相关基础知识 构造函数 FileReader() 返回一个新构造的FileReader 事件处理 FileReader.onabort 处理abort事件.该事件在读取操作被中断时触发. Fil ... 
随机推荐
- 机器学习基础系列--先验概率 后验概率 似然函数 最大似然估计(MLE) 最大后验概率(MAE) 以及贝叶斯公式的理解
			目录 机器学习基础 1. 概率和统计 2. 先验概率(由历史求因) 3. 后验概率(知果求因) 4. 似然函数(由因求果) 5. 有趣的野史--贝叶斯和似然之争-最大似然概率(MLE)-最大后验概率( ... 
- Redis的人门以及使用
			1.Redis的安装 1.1centos下安装Redis 1.1.1 安装gcc 1.1.2 安装过程 图一 图三 2.Redis的启动 2.1 前端模式启动(不推荐) 截图 2.2 后端模式(推荐 ... 
- Plastic Sprayer Manufacturer - How Does The Sprayer Work?
			The Plastic Sprayers Manufacturer stated that the sprayer is a very useful type of machine and a g ... 
- js学习:函数
			概述 函数的声明 JavaScript 有三种声明函数的方法 function 命令 function命令声明的代码区块,就是一个函数.function命令后面是函数名,函数名后面是一对圆括号,里面是 ... 
- Vue学习笔记:计算属性
			使用函数的缺点 如果我们想要将数据经过转化后再显示,或者多个数据结合起来进行显示,一般可以直接在数据渲染或者数据绑定的时候书写表达式 如果表达式过于复杂,或者逻辑太多的时候,我们可以将其封装在函数里, ... 
- VM安装linux操作系统详细教程
			1.首先我们新建一个虚拟机,先不安装操作系统,稍后再对其安装Linux系统. 新建虚拟机步骤如下: 打开VMware软件,菜单栏点击“文件(F)”–>选择“新建虚拟机(N)”,如下图1,(或者直 ... 
- java事务/springboot事务/redis事务
			java事务(数据库事务):jdbc事务--ACID springboot事务:@Transactional--ACID redis事务:命令集合 将redis事务与mysql事务对比: Mysq ... 
- SpringBoot Controller找不到视图路径
			在启动类加注解@ComponentScan("com.controller")即可,括号里表示Controller所在包名. 参考:https://blog.csdn.net/ji ... 
- 关于程序状态字寄存器PSW(Program Status Word)与多核多线程
			内核态(Kernel Mode)与用户态(User Mode) CPU通常有两种工作模式即:内核态和用户态,而在PSW中有一个二进制位控制这两种模式. 内核态:当CPU运行在内核态时,程序可以访问所有 ... 
- CF1285D Dr. Evil Underscores
			挂个链接 Description: 给你 \(n\) 个数 \(a_1,a_2,--,a_n\) ,让你找出一个 \(x\) ,使 \(x\) 分别异或每一个数后得到的 \(n\) 个结果的最大值最小 ... 
