移动端 H5 拍照 从手机选择图片,移动端预览,图片压缩,图片预览,再上传服务器
前言:最近公司的项目在做全网营销,要做非微信浏览器的wap 站 的改版,其中涉及到的一点技术就是采用H5 选择手机相册中的图片,或者拍照,再将获取的图片进行压缩之后上传。
这个功能模块主要有这5点比较难:
1手机获取相册的图片文件,拍照的图片文件,通过js 的自带的img对象,获取图片对象。
2.图片的压缩,采用canvas 画布进行压缩图片,图片的质量通过参数指定大小,数值区间在0.1-0.9之间,数值越小压缩的比例越小
3.图片的预览,将canvas画布生成的图片经过旋转平移到预览区域
4.图片的删除和上传
5.图片的存储
(一):h5 获取手机的图片
这是图片上传的页面,其中包含了:预览大图的插件smartphoto,修正ios获取图片旋转的插件,正在加载中效果的插件。(因为项目的框架使用的sgform js 提交表单,也可以用自己的form 表单提交)
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="en">
<head>
<script type="text/javascript">
var _speedMark = new Date();
</script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<meta content="" name="keywords"/>
<meta content="" name="description"/>
<title>上传图片页面</title>
<link rel="stylesheet" type="text/css" href="/css/healthRecords.css" />
<!-- 查看大图的css -->
<link rel="stylesheet" href="/css/smartphoto.css" />
<!-- 查看大图的js -->
<script type="text/javascript" src="/js/jquery-smartphoto.min.js" ></script>
<script src="/js/bootstrap.min.js" type="text/javascript" charset="utf-8"></script>
<!-- form 表单提交 -->
<script type="text/javascript" src="/js/jquery.sgfmform.js"></script>
<!-- 引用核心层插件 -->
<script type="text/javascript" src="/js/zyFile.js"></script>
<!-- 引用控制层插件 -->
<script type="text/javascript" src="/js/zyUpload.js"></script>
<!-- 修正ios图片旋转 -->
<script type="text/javascript" src="/js/exif.js"></script>
<!-- 图片正在加载中效果 -->
<link rel="stylesheet" type="text/css" href="/css/loading/load.css" media="all">
<script type="text/javascript" src="/css/loading/load-min.js"></script>
</head>
<body>
<body >
<!-- form 表单需要提交的数据,可以包含基本数据,和图片数据 -->
<form id="addExmInfo" method="post" enctype="application/x-www-form-urlencoded">
<div class="main" style="margin-bottom: 0;padding-bottom: 0;">
<ul class="write">
<li class="clearfix" onclick="openCalendar();">
<span>提交人姓名</span>
<img src="/images/main-right.png" />
<input style="display:block;" value="" id="chooseDate" name="tijianTime" onfocus="this.blur()">
</li>
</ul>
</div>
<div class="main" id="imageMain" style="border-bottom: 50px solid #f7f9fa;">
<div class="uploading" style="margin-top: 0;" id="uploadbtn" >
<img src="/images/Upload_pic_icon.png" />
<p class="uploadingTop">上传报告图片(手机上传)</p>
<p class="uploadingBottom">注:请确保图片上文字清晰可见</p>
</div>
<!-- 图片预览区 -->
<div id="wx_chooseimg" class="imgView"></div>
</div>
<!-- 浏览器类型:微信浏览器或者非微信浏览器 -->
<input value="" type="hidden" id="agentType" name="agentType"/>
<!-- 删除的图片id -->
<input type="hidden" id="delIds" name="delIds"/> <input type="button" id="submitButton" value="保存" class="btn btn-block main-btn save" />
</form> <!-- 真正触发获取手机相册的元素 -->
<input id="fileImage" style="display:none;" type="file" accept="image/*" size="30" name="imgSelected" multiple>
<input id="state" type="hidden" />
<!-- 图片加载中效果 -->
<div id="test_mask_2" class="test_mask"></div>
</body>
<script type="text/javascript">
$(document).ready(function () {
// 初始化图片插件
$("#addExmInfo").zyUpload({
width : "1000px", // 宽度
height : "500px", // 宽度
itemWidth : "120px", // 文件项的宽度
itemHeight : "100px", // 文件项的高度
url : "/core/recordExplain.upReportInfoWeb.do", // 上传文件的路径
multiple : true, // 是否可以多个文件上传
dragDrop : true, // 是否可以拖动上传文件
del : true, // 是否可以删除文件
finishDel : false, // 是否在上传文件完成后删除预览
/* 外部获得的回调接口 */
onSelect: function(files, allFiles){ // 选择文件的回调方法 },
onDelete: function(file, surplusFiles){ // 删除一个文件的回调方法
console.info(file);
},
onSuccess: function(file){ // 文件上传成功的回调方法
},
onFailure: function(file){ // 文件上传失败的回调方法
},
onComplete: function(responseInfo){ // 上传完成的回调方法
}
}); $(".img_viwer").smartPhoto({
resizeStyle: 'fit'
}); /* 这是form表单提交的js ,直接用form表单提交,不用这个js也可 */
$("#addExmInfo").sgfmform({
ajaxurl : "/core/recordExplain.upReportInfoWeb.do",
tiptype : 1,
submittype : 2,
callback : function(data,url){
if(data.returncode == 0){
alert("图片上传成功"); }else{
alert("图片上传失败");
} $("#submitButton").attr('disabled',false);
$("#submitButton").css("background-color","#05A3FF");
}
}); }); /* 图片删除绑定点击事件 */
function funBindDelEvent(index) {
if ($(".file_del").length > 0) {
confirmMsg("确认删除图片?",function(){
ZYFILE.funDeleteFile(parseInt(index), true);
}); }
}; /* 图片正在加载中 */
function mask_element_continuious() {
$.mask_element('#test_mask_2',3000);
} </script>
</html>
(二)两个核心的图片上传的js
zyFile.js 这个js 包含图片文件的获取,图片的上传
/*
*
*/
var ZYFILE = {
fileInput : "#fileImage", // 选择文件按钮dom对象
uploadInput : $("#submitButton").get(0), // 上传文件按钮dom对象
dragDrop: null, //拖拽敏感区域
url : null, // 上传action路径
uploadFile : [], // 需要上传的文件数组
lastUploadFile : [], // 上一次选择的文件数组,方便继续上传使用
perUploadFile : [], // 存放永久的文件数组,方便删除使用
fileNum : 0, // 代表文件总个数,因为涉及到继续添加,所以下一次添加需要在它的基础上添加索引
/* 提供给外部的接口 */
filterFile : function(files){ // 提供给外部的过滤文件格式等的接口,外部需要把过滤后的文件返回 },
onDelete : function(file, files){ // 提供给外部获取删除的单个文件,供外部实现删除效果 file:当前删除的文件 files:删除之后的文件 },
onProgress : function(file, loaded, total){ // 提供给外部获取单个文件的上传进度,供外部实现上传进度效果 },
onSuccess : function(file, responseInfo){ // 提供给外部获取单个文件上传成功,供外部实现成功效果 },
onFailure : function(file, responseInfo){ // 提供给外部获取单个文件上传失败,供外部实现失败效果 },
onComplete : function(responseInfo){ // 提供给外部获取全部文件上传完成,供外部实现完成效果 }, // 3.将获取的图片数据加入到文件数组中,注意下面的else 的判断,这个判断是因为ios 拍照每次获取的图片名称都是一样的,如果根据图片名称来区分是否是同一张图片将会失败。相同的图片名称只会显示一张,所以这里else 解决了ios 拍照不能拍多次的问题
funGetFiles : function(e){
var self = this;
/* 从事件中获取选中的所有文件*/
var files = e.target.files || e.dataTransfer.files;
self.lastUploadFile = this.uploadFile;
this.uploadFile = this.uploadFile.concat(this.filterFile(files)); //将过滤后的文件重新组装
var tmpFiles = [];
/*因为jquery的inArray方法无法对object数组进行判断是否存在于,所以只能提取名称进行判断*/
var lArr = []; // 之前文件的名称数组
var uArr = []; // 现在文件的名称数组
$.each(self.lastUploadFile, function(k, v){
lArr.push(v.name);
});
$.each(self.uploadFile, function(k, v){
uArr.push(v.name);
});
$.each(uArr, function(k, v){
// 获得当前选择的每一个文件 判断当前这一个文件是否存在于之前的文件当中
if($.inArray(v, lArr) < 0){ // 不存在
tmpFiles.push(self.uploadFile[k]);
}else{
/*
* 功能:因为ios每次拍照返回的文件名称都是image.jpg 所以只用文件名去重的方式会导致大于第二次的拍照的图片不能存放到uploadFile 数组中,
* 预览的时候就是没有第二次及以上的拍照的图片
* return:拍照的照片文件
*/
if((v=="image.jpg")&&(k==(uArr.length-1))){
tmpFiles.push(self.uploadFile[k]);
}
}
});
this.uploadFile = tmpFiles;
// 调用对文件处理的方法
this.funDealtFiles();
return true;
},
// 处理过滤后的文件,给每个文件设置下标
funDealtFiles : function(){
var self = this;
// 目前是遍历所有的文件,给每个文件增加唯一索引值
$.each(this.uploadFile, function(k, v){
// 因为涉及到继续添加,所以下一次添加需要在总个数的基础上添加
v.index = self.fileNum;
// 添加一个之后自增
self.fileNum++;
});
// 先把当前选中的文件保存备份
var selectFile = this.uploadFile;
// 要把全部的文件都保存下来,因为删除所使用的下标是全局的变量
this.perUploadFile = this.perUploadFile.concat(this.uploadFile);
// 合并下上传的文件
this.uploadFile = this.lastUploadFile.concat(this.uploadFile);
// 4.这里调用下面zyUpload.js 的onselect 方法,对图片进行处理
this.onSelect(selectFile, this.uploadFile);
return this;
},
// 处理需要删除的文件 isCb代表是否回调onDelete方法
// 因为上传完成并不希望在页面上删除div,但是单独点击删除的时候需要删除div 所以用isCb做判断
funDeleteFile : function(delFileIndex, isCb){
var self = this; // 在each中this指向没个v 所以先将this保留 var tmpFile = []; // 用来替换的文件数组
// 合并下上传的文件
var delFile = this.perUploadFile[delFileIndex];
// 目前是遍历所有的文件,对比每个文件 删除
$.each(this.uploadFile, function(k, v){
if(delFile != v){
// 如果不是删除的那个文件 就放到临时数组中
tmpFile.push(v);
}else{ }
});
this.uploadFile = tmpFile;
if(isCb){ // 执行回调
// 回调删除方法,供外部进行删除效果的实现
self.onDelete(delFile, this.uploadFile);
}
return true;
},
// 11.图片上传的方法
funUploadFiles : function(){
if((ZYFILE.funReturnNeedFiles().length == 0)&&($("#wx_chooseimg:has(img)" ).length==0)){
alertMsg({"type" : 3, "content" : "请上传图片", "close" : true, "timeout" : 3000}, function(){});
}else{
//点击保存之后按钮灰掉
$("#submitButton").css("background-color","gray");
$("#submitButton").attr('disabled',true);
$("#addExmInfo").submit();
}
},
// 返回需要上传的文件
funReturnNeedFiles : function(){
return this.uploadFile;
}, // 初始化
init : function(){ // 初始化方法,在此给选择、上传按钮绑定事件
var self = this; // 克隆一个自身
this.userAgent(); // 给元素添加属性 // 1.点击选择图片按钮会先触发这里的监听,点击获取图片将从这里开始
if(self.fileInput){
this.fileInput.addEventListener("change", function(e) {
2.将获取的file文件交个funGetFileses(e)这个方法进行下一步的处理,e 包含获取的图片信息和其他数据
self.funGetFiles(e);
}, false);
}
},
/*判断是android 还是 ios android 要加capture="camera" ios 不用加 ; 1 代表android 2 代表ios*/
userAgent:function(){
var agentFlag=1;
var u = navigator.userAgent;
var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; //android终端
var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端
if(isAndroid){
//$(this.fileInput).attr("capture","camera");
$("#agentType").val("isAndroid")
}else if(isiOS){
agentFlag=2;
$("#agentType").val("isIos")
}else{ }
var ua = window.navigator.userAgent.toLowerCase();
if (ua.match(/MicroMessenger/i) == 'micromessenger') {
//alert("微信");
} else {
// alert("非微信");
}
return agentFlag;
} };
zyUpload.js 这个js是图片预览,压缩的核心js,首先要整理出这个js 的逻辑比较有点困难,对于那些在js 面向对象编程方面了解不多的童鞋,不过也没多大问题多看几遍就知道了。
这个js 首先要知道这几点:
1.$.fn 这个的意思,这是jquery 的扩展方法,当给jquery 添加这个扩展方法的时候就可以使用$操作符对这个js 的方法进行调用。
2. js 的函数回调,函数回调分为同步回到和异步回调
3.returnthis.each(fucntion(){}) 这个的方法的作用是为了链式操作
4. para = $.extend(defaults, options); 这个方法是增强,就是将默认的和特定的参数进行结合,可以理解为韦恩图中的并集
/* 代码整理:懒人之家 www.lanrenzhijia.com */
(function($, undefined) {
$.fn.zyUpload = function(options, param) {
return this.each(function() {
var para = {}; // 保留参数
var self = this; // 保存组件对象
var base64Img = []; // 创建一个数组保存base64 编码的图片 var defaults = {
width : "700px", // 宽度
height : "400px", // 宽度
itemWidth : "140px", // 文件项的宽度
itemHeight : "120px", // 文件项的高度
multiple : true, // 是否可以多个文件上传
dragDrop : true, // 是否可以拖动上传文件
del : true, // 是否可以删除文件
finishDel : false, // 是否在上传文件完成后删除预览
minLimitNum : 2 * 1024 * 1024, // 当文件的大小小于这个值时,不压缩
// 单位(bit)
isResize : true, // 是否压缩文件
base64ImgUrl : [], // 经过canvas 压缩过的图片地址,组成的图片数组
/* 提供给外部的接口方法 */
onSelect : function(selectFiles, files) {
},// 选择文件的回调方法 selectFile:当前选中的文件 allFiles:还没上传的全部文件
onDelete : function(file, files) {
}, // 删除一个文件的回调方法 file:当前删除的文件 files:删除之后的文件
onSuccess : function(file) {
}, // 文件上传成功的回调方法
onFailure : function(file) {
}, // 文件上传失败的回调方法
onComplete : function(responseInfo) {
}, // 上传完成的回调方法
}; para = $.extend(defaults, options); this.init = function() {
this.addEvent();
this.createCorePlug(); // 调用核心js
}; /**
* 识别是否是手机浏览器 对选择文件夹按钮添加属性
*/
this.findIsPhone = function() {
var userAgentInfo = navigator.userAgent;
}; /*
* 点击图片进行预览
*/
this.zoomOutImage = function(imgUrl) { }; /**
* 功能:显示统计信息和绑定继续上传和上传按钮的点击事件 参数: 无 返回: 无
*/
this.funSetStatusInfo = function(files) {
var size = 0;
var num = files.length;
$.each(files, function(k, v) {
// 计算得到文件总大小
size += v.size;
}); // 转化为kb和MB格式。文件的名字、大小、类型都是可以现实出来。
if (size > 1024 * 1024) {
size = (Math.round(size * 100 / (1024 * 1024)) / 100)
.toString()
+ 'MB';
} else {
size = (Math.round(size * 100 / 1024) / 100)
.toString()
+ 'KB';
} alert("获取图片的总大小=="+size )
}; /**
* 功能:过滤上传的文件格式 参数: files 本次选择的文件 返回: 通过的文件
*/
this.funFilterEligibleFile = function(files) {
var arrFiles = []; // 替换的文件数组
for (var i = 0, file; file = files[i]; i++) {
if (file.size >= 51200000) {
alert('您选择的"' + file.name + '"图片大小过大');
} else {
// 在这里需要判断当前所有文件中
var fileExt = file.name.substr(
file.name.lastIndexOf(".")).toLowerCase();// 获得文件后缀名
if (fileExt == ".png" || fileExt == ".gif"
|| fileExt == ".jpg"
|| fileExt == ".jpeg") {arrFiles.push(file);// 如果文件是图片格式,那么就放入文件的数组
} else {
alert("您上传的图片格式不正确,请重新选择!");
}
}
}
return arrFiles;
}; /*
* 功能:对图片进行压缩
* 参数:img 是从文件系统中获取的image对象
* max_width 预览的最大宽度,
* max_height 预览的最大高度
* 返回值: src 需要填充的地址
*/
function resizeMe(img, max_width, max_height) {
// 获取照片的拍摄方向
var orient = getPhotoOrientation(img);
//当检测图片的方向为6 的时候说明图片方向右(exif.js 这么定义的),开始旋转图片
if (orient == 6) {
var canvas = document.createElement('canvas');
var width = img.width;
var height = img.height;
var exWith;
var exHeight;
canvas.width = height;
canvas.height = width;
var ctx = canvas.getContext("2d");
ctx.save();//保存状态
ctx.rotate(90 * Math.PI / 180);//把画布旋转90度
//ctx.translate(0, height); //平移
// 执行Canvas的drawImage语句
ctx.drawImage(img, 0, -height, width,height);//把图片绘制在画布,
//alert(getRatio(img))
ctx.restore();//恢复状态
return canvas.toDataURL("image/jpeg", 0.1);
} else {
var canvas = document.createElement('canvas');
var width = img.width;
var height = img.height;
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, width, height);
//alert("比例=="+getRatio(img));
return canvas.toDataURL("image/jpeg", 0.2);
} }; function getRatio(img) {
alert("kaishi")
/* if(/png$/i.test(img.src)) {
alert(2);
return 1;
}*/
var iw = img.naturalWidth, ih = img.naturalHeight;
var canvas = document.createElement('canvas');
canvas.width = 1;
canvas.height = ih;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
var data = ctx.getImageData(0, 0, 1, ih).data;
var sy = 0;
var ey = ih;
var py = ih;
while (py > sy) {
var alpha = data[(py - 1) * 4 + 3];
if (alpha === 0) {
ey = py;
} else {
sy = py;
}
py = (ey + sy) >> 1;
}
var ratio = (py / ih);
return (ratio===0)?1:ratio;
} /*
* 功能:获取照片的元信息(拍摄方向,来自exif.js 的api)
* 返回值:方向的枚举值 6 代表ios 返回的图片方向
*/
function getPhotoOrientation(img) {
var orient;
EXIF.getData(img, function() {
orient = EXIF.getTag(this, 'Orientation');
});
return orient;
} /**
* 功能: 处理参数和格式上的预览html 参数: files 本次选择的文件 返回: 预览的html
*/
this.funDisposePreviewHtml = function(file, img) {
/* alert(img.substring((img.length-100),img.length));
*/ var html = "";
// 处理不同类型文件代表的图标
var fileImgSrc = "control/images/fileType/";
if (file.type.indexOf("rar") > 0) {
fileImgSrc = fileImgSrc + "rar.png";
} else if (file.type.indexOf("zip") > 0) {
fileImgSrc = fileImgSrc + "zip.png";
} else if (file.type.indexOf("text") > 0) {
fileImgSrc = fileImgSrc + "txt.png";
} else {
fileImgSrc = fileImgSrc + "file.png";
}
var tempImgUrl=img.replace("data:image/jpeg;base64,", "");
var html = "<i class='rev' id='uploadList_"+file.index+"'>" ;
// html += "<a class='img_viwer' data-caption='"+file.index+"' data-id='"+file.index+"' data-group='0' href='"+img+"'>" ;
html += "<img id='uploadImage_" + file.index + "' src='"+img+"' class='uploadingImg' />" ;
// html += "</a>";
html += "<img id='deleteBtn_" + file.index + "' data-index='" + file.index + "' onclick='funBindDelEvent("+file.index+")' src='../images/health/delete_icon.png' class='revImg file_del'/>" ;
html += ' <input name="upload_image" style="display:none;" value="'+tempImgUrl+'">';
html += "</i>";
return html;
};
/**
* 功能:调用核心插件 参数: 无 返回: 无
*/
this.createCorePlug = function() {
$("#addExmInfo").attr("action", para.url);
var params = {
fileInput : $("#fileImage").get(0),
uploadInput : $("#submitButton").get(0),
url : $("#addExmInfo").attr("action"), filterFile : function(files) {
// 过滤合格的文件
return self.funFilterEligibleFile(files);
},
//.(紧接着zyFile.js 的流程)获取的图片在这里进行解析,获取image对象,调用图片压缩方法,调用添加预览图片的方法
onSelect : function(selectFiles, allFiles) {
para.onSelect(selectFiles, allFiles); // 回调方法
var html = '', i = 0;
// 组织预览html
var funDealtPreviewHtml = function() {
file = selectFiles[i];
mask_element_continuious();
if (file) {
//6.通过FileReader 这个js的原生对象,获取图片对象,FileReader 获取的图片数据是经过base64位编码的图片
var reader = new FileReader()
reader.onload = function(e) {
// 7.创建一个image对象
var img = new Image;
//8.接收reader中获取的图片数据,传递给iamge对象
img.src = reader.result;
var dataUrl;
img.onload = function() {
//9.调用resizeMe 的方法进行图片压缩,图片压缩采用canvase画布,用canvas 自带的图片压缩方法进行压缩,这里的图片平移和旋转将会直接影响你是否能成功的预览到图片
dataUrl = resizeMe(img, 1000,1000);
// 10.将获取的图片在预览区域进行展示
html += self.funDisposePreviewHtml(file, dataUrl);
i++;
// 再接着调用此方法递归组成可以预览的html
funDealtPreviewHtml();
} }
reader.readAsDataURL(file);
} else {
// 走到这里说明文件html已经组织完毕,要把html添加到预览区
funAppendPreviewHtml(html);
//11.图片压缩是需要一点时间的,这里是关闭loading遮罩
$.mask_close_all();
}
}; // 添加预览html
var funAppendPreviewHtml = function(html) {
$("#wx_chooseimg").append(html);
};
funDealtPreviewHtml();
// 显示转化后的统计信息
self.funSetStatusInfo(ZYFILE.funReturnNeedFiles());
},
onDelete : function(file, files) {
// 移除效果
$("#uploadList_" + file.index).fadeOut();
//并且删除节点
$("#uploadList_" + file.index).remove(); },
onComplete : function(response) {
}
};
ZYFILE = $.extend(ZYFILE, params);
ZYFILE.init();
}; /*// 绑定删除按钮事件
this.funBindDelEvent = function(index) {
if ($(".file_del").length > 0) {
alert("删除");
ZYFILE.funDeleteFile(parseInt(index), true); }
};*/
/**
* 功能:绑定事件 参数: 无 返回: 无
*/
this.addEvent = function() {
// 如果快捷添加文件按钮存在
if ($("#uploadbtn").length > 0) {
// 绑定选择事件
$("#uploadbtn").bind("click", function(e) {
ZYFILE.fileInput.click();
});
} //上传按钮绑定点击事件
if ($("#submitButton").length > 0) {
// 绑定选择事件
$("#submitButton").bind("click", function(e) {
ZYFILE.funUploadFiles();
});
} };
// 初始化上传控制层插件
this.init();
});
};
})(jQuery);
(三)java 后台接受数据存储到数据库和磁盘中
前台传递过来的数据是form 表单进行提交的,图片也包含在form表单中,提交的图片是经过base64 编码的图片,接受数据后首先要解码,再往磁盘中存数据
/*
* 上传图片的action 的方法
*/
public String uploadImg(){
JsonResponseResult result=null;
try{
String agentType=super.getRequest().getParameter("agentType"); String[] files=super.getRequest().getParameterValues("upload_image"); //图片用values 进行接受
HashMap<String, Object> param=new HashMap<>();
param.put("imgs", files);
if(agentType!=null){
if(agentType.equals("isIos")){ //android
Thread.sleep(3000); //request 接受数据的时候做一个延时,保证数据能接受完
}
}
//保存图片的service方法
boolean flag=recordExplainService.addUploadImg(param);
//TODO 这个地方的异常有问题,稍后再改
if(flag==true){
result=JsonResponseResult.createSuccess("添加图片成功");
}else{
result=JsonResponseResult.createFalied("服务器繁忙,请稍后重试!");
}
}catch(AppException app){
app.printStackTrace();
logger.info("RecordExplainAction---->upReportHosInfo");
result=JsonResponseResult.createFalied("保存图片失败!请联系管理员");
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
logger.info("RecordExplainAction---->upReportHosInfo");
result=JsonResponseResult.createFalied("保存图片失败!请联系管理员");
}
super.jsonResult = JSONObject.fromObject(result).toString();
return BaseAction.JSON;
}
impl
/**
* @throws InterruptedException
* @see 插入图片
*/
@Override
public boolean addUploadImg(HashMap<String, Object> params) throws InterruptedException {
boolean flag = false; String[] files=(String[]) params.get("imgs");
params.remove("imgs");
Integer count=0;
List<HashMap<String, Object>> imgList = new ArrayList<>();
for (int i = 0; i < files.length; i++) {
String filename = "web_"+System.currentTimeMillis() + ".jpeg"; imgList.add(img);
//前台传递过来的图片是经过base64 加密的,在这里进行解密
//String tempUrl=files[i].replace("data:image/jpg;base64,", "");
byte[] buffer=AESUtilHTBX.base64Decode(files[i]);
HashMap<String, Object> hashMap=new HashMap<>(); //保存图片到文件系统中的map
//将解密出来的字节数组填充到输入流中
InputStream fis = new ByteArrayInputStream(buffer);
hashMap.put("fileName", filename);
hashMap.put("pathType", 6);
hashMap.put("inputstr", fis); Map<String, Object> map=this.doUploadFile("1", hashMap); //公用的图片上传的方法,发送一个post请求
if(map.get("flag").equals("1")){
++count;
}
}
if(count==files.length){
//将基本数据持久化到数据库中
flag=true;
}
return flag;
}
base64 解码方法
/**
* base64解码
*
* @param base64Code
* 待解码的base64字符串
* @return 解码后的字节数组
* @throws Exception
*/
public static byte[] base64Decode(String base64Str) {
return Base64.decodeBase64(base64Str);
}
往磁盘中插入图片
/**
* 上传图片
* @return
* @throws IOException
*/
public String doPostFile() throws IOException {
JsonResponseResult result = null;
PrintWriter out = super.getResponse().getWriter();
try { String savaPath = "";
String pathType = super.getRequest().getParameter("pathType");
String fileName = super.getRequest().getParameter("fileName"); String mobile1 = super.getRequest().getParameter("mobile");
savaPath = propsLoader.props.getProperty("savePath") + mobile1 + "\\";
File tempFile = new File(savaPath);
if (!tempFile.exists()) {
tempFile.mkdirs();
} ServletInputStream input = super.getRequest().getInputStream(); // file文件流
File outFile = null;
FileOutputStream outStream = null;
byte[] buffer = new byte[1024];
int l = 0; outFile = new File(savaPath + fileName);
outStream = new FileOutputStream(outFile);
while ((l = input.read(buffer)) > 0) {
outStream.write(buffer, 0, l);
}
l = 0;
outStream.flush();
outStream.close();
input.close(); /*if(pathType.equals("3")){
//压缩图片
pictureCutService.imgCut("3", fileName);
}*/ HashMap<String, String> map = new HashMap<String, String>();
map.put("flag", "1");
map.put("fileName", fileName);
String jsonData = JSONObject.fromObject(map).toString();
out.print(jsonData); } catch (final AppException app) {
this.logger.error(app);
HashMap<String, String> map = new HashMap<String, String>();
map.put("flag", "0");
String jsonData = JSONObject.fromObject(map).toString();
out.print(jsonData); final String msg = this.getText(app.getMessage());
result = JsonResponseResult.createFalied(msg);
super.jsonResult = JSONObject.fromObject(result).toString();
} catch (final Exception e) {
this.logger.error(e);
HashMap<String, String> map = new HashMap<String, String>();
map.put("flag", "0");
String jsonData = JSONObject.fromObject(map).toString();
out.print(jsonData); final String msg = getText("com.sgfm.datacenter.action.order.OrderAction.exception");
result = JsonResponseResult.createFalied(msg);
super.jsonResult = JSONObject.fromObject(result).toString();
}
return BaseAction.JSON;
}
这里的java 方法不能直接用,需要自己去结合自己的项目去写。这里只列出来几点重要处理:图片的base64 解码、通过输入输出流往磁盘中写图片文件
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><!DOCTYPE html><html lang="en"><head><script type="text/javascript">var _speedMark = new Date();</script><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /><meta content="" name="keywords"/> <meta content="" name="description"/><title>上传图片页面</title><link rel="stylesheet" type="text/css" href="/css/healthRecords.css" /><!-- 查看大图的css --><link rel="stylesheet" href="/css/smartphoto.css" /><!-- 查看大图的js --><script type="text/javascript" src="/js/jquery-smartphoto.min.js" ></script><script src="/js/bootstrap.min.js" type="text/javascript" charset="utf-8"></script><!-- form 表单提交 --><script type="text/javascript" src="/js/jquery.sgfmform.js"></script><!-- 引用核心层插件 --><script type="text/javascript" src="/js/zyFile.js"></script><!-- 引用控制层插件 --><script type="text/javascript" src="/js/zyUpload.js"></script><!-- 修正ios图片旋转 --><script type="text/javascript" src="/js/exif.js"></script><!-- 图片正在加载中效果 --><link rel="stylesheet" type="text/css" href="/css/loading/load.css" media="all"><script type="text/javascript" src="/css/loading/load-min.js"></script></head><body><body ><!-- form 表单需要提交的数据,可以包含基本数据,和图片数据 --><form id="addExmInfo" method="post" enctype="application/x-www-form-urlencoded"><div class="main" style="margin-bottom: 0;padding-bottom: 0;"><ul class="write"><li class="clearfix" onclick="openCalendar();"><span>提交人姓名</span><img src="/images/main-right.png" /><input style="display:block;" value="" id="chooseDate" name="tijianTime" onfocus="this.blur()"></li></ul></div><div class="main" id="imageMain" style="border-bottom: 50px solid #f7f9fa;"><div class="uploading" style="margin-top: 0;" id="uploadbtn" ><img src="/images/Upload_pic_icon.png" /><p class="uploadingTop">上传报告图片(手机上传)</p><p class="uploadingBottom">注:请确保图片上文字清晰可见</p></div><!-- 图片预览区 --><div id="wx_chooseimg" class="imgView"></div></div><!-- 浏览器类型:微信浏览器或者非微信浏览器 --><input value="" type="hidden" id="agentType" name="agentType"/><!-- 删除的图片id --><input type="hidden" id="delIds" name="delIds"/><input type="button" id="submitButton" value="保存" class="btn btn-block main-btn save" /></form><!-- 真正触发获取手机相册的元素 --> <input id="fileImage" style="display:none;" type="file" accept="image/*" size="30" name="imgSelected" multiple> <input id="state" type="hidden" /> <!-- 图片加载中效果 --> <div id="test_mask_2" class="test_mask"></div></body><script type="text/javascript">$(document).ready(function () {// 初始化图片插件$("#addExmInfo").zyUpload({width : "1000px", // 宽度height : "500px", // 宽度itemWidth : "120px", // 文件项的宽度itemHeight : "100px", // 文件项的高度url : "/core/recordExplain.upReportInfoWeb.do", // 上传文件的路径multiple : true, // 是否可以多个文件上传dragDrop : true, // 是否可以拖动上传文件del : true, // 是否可以删除文件finishDel : false, // 是否在上传文件完成后删除预览/* 外部获得的回调接口 */onSelect: function(files, allFiles){ // 选择文件的回调方法},onDelete: function(file, surplusFiles){ // 删除一个文件的回调方法console.info(file);},onSuccess: function(file){ // 文件上传成功的回调方法},onFailure: function(file){ // 文件上传失败的回调方法},onComplete: function(responseInfo){ // 上传完成的回调方法}});
$(".img_viwer").smartPhoto({ resizeStyle: 'fit' }); /* 这是form表单提交的js ,直接用form表单提交,不用这个js也可 */$("#addExmInfo").sgfmform({ ajaxurl : "/core/recordExplain.upReportInfoWeb.do", tiptype : 1, submittype : 2, callback : function(data,url){ if(data.returncode == 0){ alert("图片上传成功"); }else{ alert("图片上传失败"); } $("#submitButton").attr('disabled',false); $("#submitButton").css("background-color","#05A3FF"); }});});
/* 图片删除绑定点击事件 */function funBindDelEvent(index) { if ($(".file_del").length > 0) { confirmMsg("确认删除图片?",function(){ ZYFILE.funDeleteFile(parseInt(index), true); }); }};
/* 图片正在加载中 */function mask_element_continuious() {$.mask_element('#test_mask_2',3000);}</script></html>
移动端 H5 拍照 从手机选择图片,移动端预览,图片压缩,图片预览,再上传服务器的更多相关文章
- 移动前端—H5实现图片先压缩再上传
在做移动端图片上传的时候,用户传的都是手机本地图片,而本地图片一般都相对比较大,拿iphone6来说,平时拍很多图片都是一两M的,如果直接这样上传,那图片就太大了,如果用户用的是移动流量,完全把图片上 ...
- 图片上传oss--先拿server端签名再上传oss,返回id值
目前项目oss阿里云存储图片,图片上传主要步骤是:前端从服务端拿到签名signature,再上传到oss上busket里,上传成功返回图片id (imgId),最后再给server端: 注:官网上有个 ...
- 移动端图片上传解决方案localResizeIMG先压缩后ajax无刷新上传
现在科技太发达,移动设备像素越来越高,随便一张照片2M+,但是要做移动端图片上传和pc上略有不同,移动端你不能去限制图片大小,让用户先处理图片再上传,这样不现实.所以理解的解决方案就是在上传先进行图片 ...
- HTML5调用本地摄像头画面,拍照,上传服务器
实现功能和适用业务 采集本地摄像头获取摄像头画面,拍照保存,上传服务器: 前端上传图片处理,展示,缩小,裁剪,上传服务器 实现步骤 调取本地摄像头(getUserMedia)/上传图片,将图片/视频显 ...
- JavaScript把项目本地的图片或者图片的绝对路径转为base64字符串、blob对象在上传
主题: JavaScript把项目本地的图片或者图片的绝对路径转为base64字符串.blob对象在上传. 用处: 从本地选择图片上传,如项目规定只能选择本项目文件夹下的图像上传为头像等. 主要思想: ...
- 前台利用jcrop做头像选择预览,后台通过django利用Uploadify组件上传图最终使用PIL做图像裁切
之前一直使用python的PIL自定义裁切图片,今天有需求需要做一个前端的选择预览页面,索性就把这个功能整理一下,分享给大家. 实现思路: 1.前端页面: 用户选择本地一张图片,然后通过鼠标缩放和移动 ...
- 斗牛app上架应用宝、牛牛手机游戏推广、百人牛牛app应用开发、棋牌游戏上传、手游APP优化
联系QQ:305-710439斗牛app上架应用宝.牛牛手机游戏推广.百人牛牛app应用开发.棋牌游戏上传.手游APP优化 iOS开发iPhone/iPad平台安卓手机软件开发机型覆盖范围 超过113 ...
- vue代码上传服务器后背景图片404解决方法
问题:代码上传服务器后,图片404,使用的font-awesome图标也是404 解决办法: 如果你用了vue-cil,那么在build目录下找到utils.js中的ExtractTextPlugin ...
- ios中摄像头/相册获取图片压缩图片上传服务器方法总结
本文章介绍了关于ios中摄像头/相册获取图片,压缩图片,上传服务器方法总结,有需要了解的同学可以参考一下下. 这几天在搞iphone上面一个应用的开发,里面有需要摄像头/相册编程和图片上传的问 ...
随机推荐
- java读取Excel表格中的数据
1.需求 用java代码读取hello.xls表格中的数据 2.hello.xls表格 3.java代码 package com.test; import java.io.File; import j ...
- Selenium 工具介绍、安装、配置及使用
概念 selenium是一款跨平台开源工具,用于web的功能完整性測试,想做压力測试的能够换软件了 先看一眼官方站 http://docs.seleniumhq.org/download/ 首先要FQ ...
- oracle多表关联多字段update
多表关联多字段update 有代码有J8: update spatial_references set( auth_name, auth_srid, falsex, falsey, xyunits, ...
- brctl和虚拟网桥
1 创建空的虚拟网桥 brctl addbr br0 这个时候可以认为该虚拟网桥有多个虚拟接口,但是没有实际的网卡接口和该虚拟网桥相连的. 2 将eth0网卡连接到br0 网卡只有一个接口,这个接口是 ...
- win10复制粘贴 失效
win10复制粘贴 DISM.exe /Online /Cleanup-image /Restorehealth https://social.technet.microsoft.com/Forums ...
- 设计模式-(6)适配器 (swift版)
用来解决接口适配问题的三种模式:适配器模式,桥接模式,外观模式. 一,概念 适配器模式,将一个类的结构转换成用户希望的另一个接口,使得原本接口不兼容的类能在一起工作.换句话说,适配器模式就是链接两种不 ...
- UVA796 Critical Links —— 割边(桥)
题目链接:https://vjudge.net/problem/UVA-796 In a computer network a link L, which interconnects two serv ...
- hash与map的区别联系应用
一,hashtable原理: 哈希表又名散列表,其主要目的是用于解决数据的快速定位问题.考虑如下一个场景. 一列键值对数据,存储在一个table中,如何通过数据的关键字快速查找相应值呢?不要告诉我一个 ...
- Activity并行网关和排他网关
说一说activiti中的排他网关和并行网关 activiti工作流中我们经常用到的网关有两种: 1. Exclusive Gateway 排他网关 排他网关.png 排他网关(也叫异或(XOR)网关 ...
- Oracle sql执行计划解析
Oracle sql执行计划解析 https://blog.csdn.net/xybelieve1990/article/details/50562963 Oracle优化器 Oracle的优化器共有 ...