H5使用Base64和Canvas上传图片

    最近在做一个班级通知的H5页面,从拿到原型到开发出完整功能的过程中,解决了一些问题,现在记录一下发布通知时遇到的难点和解决方法。

一、调用手机摄像头和手机相册选取照片

1、由于使用的是最接近原生Android的mui框架,所以最开始是准备用mui的plus来调用camera和gallery方法,但是后来发现一直出现plusReady未定义的错误,写在plusReady中的代码没有执行。
    在DCloud中找到原因如下,普通浏览器里没有plus环境,只有HBuilder真机运行和打包后才能运行plus api,且个人尝试在真机中也不能运行。公司使用mui在初始阶段,本人更是小白,到了这里最后还是决定使用JS来实现照片的选择和上传。

2、使用 input标签调用摄像头和相册,accpet表示打开系统文件目录,capture表示可捕获到的系统设备,multiple表示多选。
  <input type="file" accept="image/*" > // 打开相机或相册
② <input type="file" accept="image/*" capture="camera"> // 打开相机,可以拍照
③ <input type="file" accept="video/*" capture="camcorder"> // 打开录像机,可以录像
④ <input type="file" accept="audio/*" capture="microphone"> // 打开录音机,可以录音
     选择input标签后,觉得和已有的UI风格相差很大,界面显示如下:
                                             
     可以设置  opacity:0 将已有的input格式隐藏,然后将文本信息居中显示。
<input style="opacity:0;width:1px" type="file" id="file_input" accept="image/*" multiple>选择图片</input>

二、Base64。

1、简介。
     Base64是一种用64个字符【 A-Z  a-z  0-9  + /】来表示任意二进制数据的方法,常用于在URL、Cookie、数字证书签名或网页中传输少量二进制数据。
     将每3个字节Byte即3*8=24bit,转化为4组6个二进制位,然后在6位的前面补两个0,形成一个8位的字节,若剩下的字符不足3个字节,则用0填充,输出字符使用'=',因此编码后输出的文本末尾可能会出现1或2个'='【 解码时会自动去掉】。 通过查表获得4个编码后的字符,由于Base64把3字节的二进制数据编码为4字节的文本数据,所以长度增加33%左右。
                 

2、为什么使用Base64。
① 减少HTTP请求数,提高页面加载速度。对于只有几个KB大小的图片,进行HTTP请求附带的额外信息可能比图片本身还大,当请求量大时,数据传输会变慢【以 data:image/*;base64开头】。
② 将图片使用Base64编码后,可通过URL直接解码查看图片,即内嵌到网页中而不是去请求并加载进来。
③ 可以防止因为一些相对路径等问题导致的图片404错误。

3、Base64的缺点。
① Base64是一种通过查表的编码方法,不能用于加密,即使使用自定义的编码表也不行。
② 当需要传输的图片在1M以上时【未确定临界值】,在网页中加载Base64会变得很慢,此时需对图片进行压缩处理。

三、使用Base64和Canvas保存图片的编码。

1、使用 FileReader将文件转化为Base64编码。
① 在H5中使用FileReader把文件读取到内存,并读取文件中的数据,以便对图片进行预览。
② FileReader的 readAsDataURL方法可以将读取的文件数据以DataURL的形式即Base64存储,可在客户端直接显示。

2、使用Canvas对图片重新绘制并设置压缩质量。
① 将图片转化成Base64后,可以绘制到Canvas上并对图片进行编辑操作。
② 当编辑或压缩完成后,可以使用Canvas的 toDataURL方法输出新图片的Base64数据。

HTML:
<div class="row mui-input-row ">
			<textarea id="container" name="txt" class="mui-input-clear question"
				placeholder="请输入正文(必填,最多输入500个汉字)"></textarea>
			<div class="add_picture_div">
				<a href="#picture" class="add_picture"><img src="${base}/res/images/add.png"></a>
			</div>

			<div id="picture" class="mui-popover mui-popover-action mui-popover-bottom">
				<ul class="mui-table-view">
					<li class="mui-table-view-cell">
						<input style="opacity:0;width:1px" type="file" id="file_input" accept="image/*" multiple>选择图片</input>
					</li>
					<li class="mui-table-view-cell"><a href="#"><b>取消</b></a></li>
				</ul>
			</div>
		</div>
JavaScript:
var myHtml="";
		window.onload = function(){  
		    var result,input = document.getElementById("file_input");  
		    if(typeof FileReader==='undefined'){
		        result.innerHTML = "抱歉,你的浏览器不支持 FileReader";  
		        input.setAttribute('disabled','disabled');  
		    }else{  
		        input.addEventListener('change',readFile,false);  
		    } 
		    function readFile(){  
	            var iLen = this.files.length;  
		        for(var i=0;i<iLen;i++){  
		            if (!input['value'].match(/.jpg|.gif|.png|.bmp/i)){  //判断上传文件格式  
		                return alert("上传的图片格式不正确,请重新选择");  
		            }  
		            var reader = new FileReader();  
		            var fileName = this.files[i].name; 	  // 获取文件名
		            var fileType = this.files[i].type;	  // 文件后缀
		            reader.readAsDataURL(this.files[i]);  // 转成base64,此方法执行完后,base64数据储存在reader.result里 
		            reader.onload = function(e){
		            	var image = new Image();		  // 创建一个image对象,供canvas绘图使用	
		            	image.src = this.result;		  // this.result即base64的数据
		            	image.onload = function(){
		            		var scale = 1;
		            		var cvs = document.createElement('canvas');
		            		if(this.width > 300 || this.height > 300){
		            			if(this.width > this.height)
		            				scale = 300 / this.width;
		            			else scale = 300 / this.height;
		            		}
		            		cvs.width = scale * this.width;		// 计算等比缩小后图片宽
		            		cvs.height = scale * this.height;
		            		var ctx = cvs.getContext('2d');		// 返回一个用于在画布上二维绘图的环境
		            		ctx.drawImage(this,0,0,cvs.width,cvs.height);
		            		var newImage = cvs.toDataURL(fileType,0.7);	// 重新生成图片Base64,压缩质量即压缩率为0.7
		            		myHtml += "<br><p style='text-align:center;'><img src='"+newImage+"'/></p>";
			                var aHtml = '<a href="#" class="add_picture"><img src="'+newImage+'"/></a>';
			                $(".add_picture_div").append(aHtml);
		            	}
		            }
		        }  
		    }  
		}
jQuery:
$.ajax({
				url:'${base}/classedit/notice/noticePublish',
				type:"post",
				data:{
					ownerId:$("#clsId").val(),		// 班级ID
					listTitle:$("#title").val(),
					txt:$("#container").val()+myHtml
				},
				success:function(result){
					console.info(result);
					if(result.status == true){
						alert("发布成功!");
						window.location.href='toNoticeListPage?userId=${userId!'' }';
					}else{
						alert("发布失败!");
						window.location.reload();
					}
				}
			});
让发布的通知以原样显示到详情页中,可以使用<pre>标签格式化 freemarker 取出的数据。pre 元素可定义预格式化的文本,被包围在 pre 元素中的文本通常会保留空格和换行符,存储到数据库中的数据会以原格式显示到页面中。
<div class="mui-media-body">
			<h4>${cmsContentExt.title }</h4>
			<h6 class="mt15">${cmsContentExt.author } ${cmsContentExt.releaseDate?string("yyyy-MM-dd HH:mm:ss") }</h6>
			<h6 class="mui-ellipsis mt20 fong_gray">
				<#if listCD ??>
					<#list listCD as list>
						${list.className }
					</#list>
				</#if>
			</h6>
			<span class="mt20 fong_gray"><pre>${cmsContentTxt.txt}</pre></span>
		</div>

3、效果图:
① 发布通知:
                                             
② 按Scale等比缩放图片像素
                                                               
③ 使用img{max-width:100%;} 设置,若图片大于屏宽则100%显示,图片小于屏宽则显示原图。
                                                      
     至此,H5页面发布通知功能开发完成。


猜你喜欢

转载自blog.csdn.net/a515557595_xzb/article/details/79098679