一、简单说明

  本头像编辑器主要实现了图片的上传、显示(不溢出父窗口)、旋转、裁剪功能!

  1. 图片的上传用到的是异步上传,页面不进行刷新,原理是通过JQuery的异步提交+SpringMVC的上传
  2. 上传完毕后,在显示的时候,如果图片的长宽都比父窗口小,则垂直 水平 居中显示在父窗口中,否则,对图片按照宽高比进行缩放,直到长宽都不溢出!
  3. 旋转功能,这块有待改进!我在上传一张图片的时候,直接在后台将90度,180度,270度的也上传了,然后,裁剪完成后,保存头像之后,将不用的都删除!这儿确实不太现实!这里推荐用HTML5的canvas实现!
  4. 裁剪功能。用到了JQuery的一个插件。参见区域选择完成后,会自动返回 x,y,w,h。这里的x,y,w,h还需要经过处理,因为你前面可能进行了缩小显示,这里需要让x,y,w,h还原到未缩放前的坐标。然后,在后台中,通过这些坐标,截取图片中 (x,y) 宽:w 高:h的图片!

二、效果图:

  1. 初始化界面

2.选择图片

3.显示选择的图片

4.旋转图片

5.拖动鼠标选择裁剪框

6.保存头像,并且显示

三、代码实现

1.图片的选择以及上传

我们肯定都见过自带的上传按钮,这个按钮比较难看。,这个是不是比上一张强多了!其实就是 一个buton按钮,然后将上面的<input type="file">隐藏了,然后当点击button按钮的时候,调用file的事件。这样就实现了和点击file一样的效果。

<input type="button" value="选择头像" class="bt"><font color="#999999"><span>&nbsp;&nbsp;支持jpg、jpeg、gif、png、bmp格式的图片</span>
<input type="file" id="file" name="file"> <!--可以在css中设置隐藏属性或者在js中设置-->
 $(".bt").click(function(){
$("#file").click();
});

这样,就实现了弹出选择框的功能。接下来需要实现的就是选择图片。这里需要注意的是,现在由于浏览器的安全机制,默认情况下,你是不能直接访问本地图片地址,例如<img src="d:1.png">这种情况下在jsp页面中完全不能用,除非你对浏览器进行设置。但是这么是不合理的。所以,最好能够上传图片到服务器。

SpringMVC中,上传图片用到了"commons-fileupload-1.2.2.jar"这个插件,如果要使用图片上传,需要注意一下几点:

  • 在SpringMVC的配置文件中,添加一下内容
 <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8" />
<property name="maxUploadSize" value="10485760000" />
<property name="maxInMemorySize" value="40960" />
</bean>
  • 在HTML中,表单的提交中,需要设置'enctype="multipart/form-data" '

上传完图片后,这里不像再去跳转,所以,这里我用到了异步上传,所谓的异步上传,其实就是异步提交表单。异步提交用得到了JQuery的异步提交插件:"jquery-form.js";

  

 $("#file").change(function(){
var options = {
url : "upload/image", //这个url是异步提交的路径,这里对应的是上传图片的路径
dataType : 'json',
contentType : "application/json; charset=utf-8",
success : function(data) {
//上传成功后,需要进行哪些处理,比方说这里实现的是显示图片
};
$("#uploadImgForm").ajaxSubmit(options);
});

  

如上所示,就实现了图片的异步上传,那么后台是如何处理?这里 是先上传原图,然后再用一个旋转类,将该图片90度,180度,270度的图片保存成功。以供旋转使用,这里是一个大问题,其实我们可以使用html5的canvas实现,这样只需要上传一张图片呢。

 @Controller
@RequestMapping(value="upload")
public class FileUploadController { @RequestMapping(value="image")
public void fileUpload(MultipartHttpServletRequest request, HttpServletResponse response) {
Map<String, Object> resultMap = new HashMap<String, Object>();
String newRealFileName = null;
try {
MultipartHttpServletRequest multipartRequest = request;
CommonsMultipartFile file = (CommonsMultipartFile) multipartRequest.getFile("file");
// 获得文件名:
String realFileName = file.getOriginalFilename();
if(file.getSize()/1024>=5*1024){
resultMap.put("status", 1);
resultMap.put("message", "图片不能大于5M");
}else{
System.out.println("获得文件名:" + realFileName);
newRealFileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + realFileName.substring(realFileName.indexOf("."));
// 获取路径
String ctxPath = request.getSession().getServletContext().getRealPath("//") + "//temp//";
System.out.println(ctxPath);
// 创建文件
File dirPath = new File(ctxPath);
if (!dirPath.exists()) {
dirPath.mkdir();
}
File uploadFile = new File(ctxPath +"now"+ newRealFileName);
FileCopyUtils.copy(file.getBytes(), uploadFile);
// request.setAttribute("files", loadFiles(request)); //获取图片的宽度和高度
InputStream is = new FileInputStream(ctxPath +"now"+ newRealFileName);
BufferedImage buff = ImageIO.read(is);
int realWidth=buff.getWidth();
int realHeight = buff.getHeight();
is.close();
//接下来将图片的四个旋转进行保存
//1.向左旋转90度
String onenewRealFileName = "one"+newRealFileName;
Thumbnails.of(uploadFile).size(realWidth, realHeight).rotate(90).toFile(new File(ctxPath+onenewRealFileName)
);
//2.向左旋转180度
String twonewRealFileName="two"+newRealFileName;
Thumbnails.of(uploadFile).size(realWidth, realHeight).rotate(180).toFile(new File(ctxPath+twonewRealFileName)
);
//3.向左旋转270度
String threenewRealFileName="thr"+newRealFileName;
Thumbnails.of(uploadFile).size(realWidth, realHeight).rotate(270).toFile(new File(ctxPath+threenewRealFileName)
);
resultMap.put("status", 0);
resultMap.put("fileName", "now"+newRealFileName);
}
} catch (Exception e) {
resultMap.put("status", 1);
resultMap.put("message", "图片上传出错");
System.out.println(e);
} finally {
PrintWriter out = null;
try {
out = response.getWriter();
} catch (IOException e) {
e.printStackTrace();
}
//必须设置字符编码,否则返回json会乱码
response.setContentType("text/html;charset=UTF-8");
out.write(JSONSerializer.toJSON(resultMap).toString());
out.flush();
out.close();
}
}
80}

2.图片的展示

图片上传成功后,然后讲一个json字符串返回到前端,其中包括了上传完后图片的路径。下面就需要展示图片。想一下,上传的图片大小不一,DIV如何才能包住图片呢?如果在<img>设置图片的宽度=div宽度,图片的高度=div高度,这样的话,对图片会进行不等比例拉伸或者缩小,这样图片就完全变形,看上去肯定很不美观。我们需要做的就是,让图片按照原来的比例进行缩放。这样,图片看上去没有拉伸的那种效果。

这种请款下,就得分四种情况讨论:

  • 图片的原始宽和高都比DIV的宽和高小,那么这种情况下,图片需要在水平和垂直上居中显示

我是通过margin-top属性和margin-left属性来对图片进行设置,以使其居中。margin-top:(DIV高-图片高)/2    margin-left:(DIV宽-图片宽)/2

  • 图片的原始宽<DIV的宽,图片的原始高>DIV的高,这种情况下,计算图片的原始宽高比compareImage,然后,让图片的高=DIV高度,再根据图片的原始宽高比,算出现在图片的宽,也就是图片缩放的效果;

这种情况下margin-top:0 margin-left:(DIV宽度-图片现宽)/2让图片居中;

  • 图片的原始宽>DIV的宽,图片的原始高<DIV的高,这种情况下,计算图片的原始宽高比compareImage,然后,让图片的宽=DIV宽,再根据图片的原始宽高比,算出现在图片的高,也就是图片缩放的效果;后面两种效果图就不展示了
  • 图片的原始宽>DIV的宽,图片的原始高>DIV的高,这种情况比较复杂,因为你要比较谁最后溢出,例如,因为图片的宽和高都溢出,所以,如果图片你的宽==DIV的宽度,那么按照比例缩放后,你必须保证图片现高<=DIV的高度。也就是必须保证图片的宽和高都不溢出。

好,既然长宽都固定好了,肯定图片不会溢出DIV,那么现在就需要在DIV中展示效果,这里用到了"jquery.Jcrop.min.js"这个插件来实现,图片加载成功的时候,裁剪框也跟着显示出来。

也许你不愿意去看懂"jquery.Jcrop.min.js"的代码,说实话,我也不愿意去看,用法:

  • 初始化一个对象,这个对象在我的"jQuery.UtrialAvatarCutter.js"中有说明,下面有提供下载地址,以及源码

这里的picture_original指的是图片外面DIV的id ,这里的resourceImage是图片的id

这里的bigImage 和 smallImage指的是头像预览两个DIV框

var cutter = new jQuery.UtrialAvatarCutter(
{
//主图片所在容器ID
content : "picture_original,resourceImage", //缩略图配置,ID:所在容器ID;width,height:缩略图大小
purviews : [{id:"bigImage",width:100,height:100},{id:"smallImage",width:50,height:50}],
}
);

    

  • 在图片加载完成的时候,这里,也就是异步提交完成的时候,进行的处理,在上面异步提交图片的时候有说明,success()里面的逻辑代码没有写,下面就是要处理的业务逻辑,也就是设置图片的宽和高,显示裁剪框
 success : function(data) {

     $(".page-left-center").hide();
$(".page-left-center1").show();
var imagepath="temp/"+data.fileName;
$("#resourceImage").attr("src", imagepath).load(function(){
//获取图片的真实大小:
var realWidth;
var realHeight;
realWidth = parseInt(this.width);
realHeight =parseInt(this.height);
rWidth=realWidth;
rHeight=realHeight;
realCompare=parseFloat(realWidth)/parseFloat(realHeight);
//让图片适应div大小
console.info("图片的真实宽度:"+realWidth);
console.info("图片的真实高度:"+realHeight);
if(realWidth<divWidth){
if(realHeight<divHeight){
console.info("进入了宽小,高小");
imageWidthAfter=realWidth;
imageHeightAfter=realHeight;
shengHeight=parseInt((divHeight-imageHeightAfter)/2);
$("#resourceImage").css("margin-top",shengHeight);
shengWidth=parseInt((divWidth-imageWidthAfter)/2);
$("#resourceImage").css("margin-left",shengWidth);
suoCompare=1;
}else{
console.info("进入了宽小,高大");
imageHeightAfter=divHeight;
imageWidthAfter=imageHeightAfter*realCompare;
shengWidth=parseInt((divWidth-imageWidthAfter)/2);
$("#resourceImage").css("margin-left",shengWidth);
shengHeight=0;
suoCompare=parseFloat(realHeight)/parseFloat(divHeight);
}
}else{
if(realHeight<divHeight){
console.info("进入了宽大,高小");
imageWidthAfter=divWidth;
imageHeightAfter=parseFloat(divWidth/parseFloat(realCompare));
shengHeight=parseInt((divHeight-imageHeightAfter)/2);
$("#resourceImage").css("margin-top",shengHeight);
shengWidth=0;
suoCompare=parseFloat(realWidth)/parseFloat(divWidth);
}else{
console.info("进入了高大,宽大但处理后不满");
//刚开始假如高满宽不满,那么,根据高得出宽
imageHeightAfter=divHeight;
imageWidthAfter=imageHeightAfter*realCompare;
//处理完后,宽还是溢出的话,说明以宽为基准
console.info("处理后的宽度:"+imageWidthAfter);
if(imageWidthAfter>divWidth){
console.info("进入到了宽大,高大但高不满");
imageWidthAfter=divWidth;
imageHeightAfter=parseFloat(divWidth/parseFloat(realCompare));
shengHeight=parseInt((divHeight-imageHeightAfter)/2);
$("#resourceImage").css("margin-top",shengHeight);
shengWidth=0;
suoCompare=parseFloat(realWidth)/parseFloat(divWidth);
}else{
shengWidth=parseInt((divWidth-imageWidthAfter)/2);
$("#resourceImage").css("margin-left",shengWidth);
shengHeight=0;
suoCompare=parseFloat(realHeight)/parseFloat(divHeight);
}
}
} $("#resourceImage").show();
$(".jcrop-holder").remove();
console.info("shengHeight:"+shengHeight);
$("#resourceImage").width(imageWidthAfter);
$("#resourceImage").height(imageHeightAfter);
cutter.init();
$(".jcrop-holder").css("margin-top",shengHeight);
$(".jcrop-holder").css("margin-left",shengWidth);
$(".jcrop-holder img").css("margin-top","0px");
$(".jcrop-holder img").css("margin-left","0px");
});
$("#rechose").show();
$(".btsave").attr("disabled",false);
$(".btsave").addClass("btsaveclick");
}
};

    

3.图片的旋转

这块实现的不是特别理想,前面也说过了,就是刚开始上传的时候,上传四张,0度,90度,180度,270度。最近查看了HTML5的canvas,觉得这个挺好的。以后会试着通过HTML5来实现。旋转的实现很简单,每点击的时候,设置一个表示step 然后,通过switch进行判断,如果switch=0,则设置image的src=0度的图片,一次往后推。设置完成后,然后通过 cutter.init();重新显示裁剪框以及图片!

4.图片的裁剪

图片的裁剪原理,其实就是在原有图片的基础上,从(x,y)点开始,截取长度为宽度为w,高度为h的区域。

注意:由于显示的时候,不让图片超出DIV,所以,对图片进行了缩放,所以一这里你的x,y,w,h是还原后的。

 $(".btsave").click(function(){
var data = cutter.submit();
var trueX=parseInt(parseFloat(data.x)*suoCompare);
var trueY=parseInt(parseFloat(data.y)*suoCompare);
var trueW=parseInt(parseFloat(data.w)*suoCompare);
var trueH=parseInt(parseFloat(data.h)*suoCompare);
var message=trueX+","+trueY+","+trueW+","+trueH+","+data.s;
console.info(message);
$("#cutHeader").val(message);
$("#formSubmit").submit();
});

  如上所示,提交到后台,在后台进行处理,后台逻辑如下:

 @RequestMapping(value="cutImage")
public ModelAndView cutImage(HttpServletRequest request, HttpServletResponse response) throws IOException {
String message=request.getParameter("header");
int x=Integer.parseInt(message.split(",")[0]);
int y=Integer.parseInt(message.split(",")[1]);
int w=Integer.parseInt(message.split(",")[2]);
int h=Integer.parseInt(message.split(",")[3]);
String src=message.split(",")[4].split("\\?")[0];
//
// 获取路径
String ctxPath = request.getSession().getServletContext().getRealPath("//") ;
System.out.println(ctxPath);
// 创建文件
src =ctxPath+"\\"+src;
String last=src.substring(src.lastIndexOf ("."));
File dirPath = new File(ctxPath+"\\header");
if (!dirPath.exists()) {
dirPath.mkdir();
}
String dest = ctxPath+"\\header\\"+new SimpleDateFormat("yyyyMMddHHmmss").format(new Date())+last;
String headerurl="header/"+new SimpleDateFormat("yyyyMMddHHmmss").format(new Date())+last;
boolean isSuccess=cutImageHeader(src,dest,x,y,w,h);
if(isSuccess){
return new ModelAndView("showHeader","image",headerurl);
}else{
return new ModelAndView("changeHeader");
} } public static boolean cutImageHeader(String src,String dest,int x,int y,int w,int h) throws IOException{
String last=src.substring(src.lastIndexOf (".")+1);
System.out.println("*****"+last);
Iterator iterator = ImageIO.getImageReadersByFormatName(last);
ImageReader reader = (ImageReader)iterator.next();
InputStream in=new FileInputStream(src);
ImageInputStream iis = ImageIO.createImageInputStream(in);
reader.setInput(iis, true);
ImageReadParam param = reader.getDefaultReadParam();
Rectangle rect = new Rectangle(x, y, w,h);
System.out.println("x:"+x+";y:"+y+";w:"+w+";h:"+h);
param.setSourceRegion(rect);
BufferedImage bi = reader.read(0,param);
boolean isSuccess=ImageIO.write(bi, last, new File(dest));
return isSuccess;
}

  

SpringMVC+JQuery实现头像编辑器的更多相关文章

  1. SpringMvc+jquery easyui模块开发7步骤

    搞了一段java的开发,总结出模块开发经验: SpringMvc+jquery easyui模块开发7步骤:1) 数据表(table):                定义表结构并创建数据表t_use ...

  2. JQuery轻量级网页编辑器 选中即可编辑

    目前流行的可视化网页编辑器非常多,像ckeditor.kindeditor.tinyeditor等,虽然功能都非常强大,但是体积都比 较庞大,使用起来也不是很方便.今天我们分享一款基于jQuery的轻 ...

  3. Jquery 概念性内容编辑器

      概念性jQuery内容编辑器,这是一款非常有特色的jQuery编辑器,该编辑器支持文字.列表.视频.引用等功能,是一款小巧简洁,富有个性化的jQuery内容编辑器插件. 代码: <!doct ...

  4. springmvc + jquery easyui实现分页显示

    如有不明确的地方,戏迎增加QQ群交流:66728073      推荐一本Java学习的书:深入理解Java7 一,下载并导入jquery easyui的导 <link rel="st ...

  5. Spring Data Jpa+SpringMVC+Jquery.pagination.js实现分页

    本博客介绍基于Spring Data这款orm框架加上Jquery.pagination插件实现的分页功能. 介绍一下Spring Data框架 spring Data : Spring 的一个子项目 ...

  6. SpringMVC+jquery.uploadify 上传文件

    前言 以前用Asp.net MVC+uploadify上传文件,最近学习SpringMVC,所以就用SpringMVC+uploadify做个上传文件的demo. 刚开始用form表单的方式提交,在C ...

  7. SpringMVC -jquery实现分页

    效果图: 关键类的代码: package:utils: SpringUtil.java 通过jdbcTemplate连接oracle数据库 package com.utils; import org. ...

  8. 基于bootstrsp的jquery富文本编辑器的手冊说明

    重点:当在页面插入文本编辑器后.无法用js/jq的方式去将某些值写入到文本编辑器.如:$("textarea").val("111");$("text ...

  9. SpringMVC+Jquery实现Ajax

    一.什么是Ajax? Ajax:异步的JavaScript和Json(这里XML改为了Json): 作用:用于完成网页局部刷新功能(修改少量数据只用局部刷新,不用再整个网页重新加载): 二.Sprin ...

随机推荐

  1. element-vue-koa2-mysql实现文件上传

    友情提示:这篇博客不会详细说明搭建过程 阅读群体建议:第一次使用node或者koa2写文件上传或者下载,因为你不知道用fs的哪个方法,我也是从fs里试水试了一天,各种百度才搞出来的,特别学过java的 ...

  2. Promise事件比timeout优先

    Promise, setTimeout 和 Event Loop 下面的代码段,为什么输出结果是1,2,3,5,4而非1,2,3,4,5?(function test() { setTimeout(f ...

  3. PHP运行出现Notice : Use of undefined constant 的解决办法

    这些是 PHP 的提示而非报错,PHP 本身不需要事先声明变量即可直接使用,但是对未声明变量会有提示.一般作为正式的网站会把提示关掉的,甚至连错误信息也被关掉 关闭 PHP 提示的方法 搜索php.i ...

  4. UEditor (富文本编译器)

    下载网址:https://ueditor.baidu.com/website/download.html 开发文档:http://fex.baidu.com/ueditor/

  5. c# ref与out用法

    class Program { static void Main(string[] args) { //普通 : ; ); Console.WriteLine("/*普通:*/") ...

  6. leetcode1028

    class Solution(object): def __init__(self): self.List = list() def rdfs(self,S): if S != '': length ...

  7. nexus的安装和简介(3)

    从私服下载jar包  没有配置nexus之前,如果本地仓库没有,去中央仓库下载,通常在企业中会在局域网内部署一台私服服务器,有了私服本地项目首先去本地仓库找jar,如果没有找到则连接私服从私服下载ja ...

  8. 23.Hibernate-基础.md

    目录 1. ORM和Hibernare 2. 基本开发 2.1 lib 2.2 写对象和引入对象映射 2.2.1 写对象类文件 2.3 配置文件 2.3.1 配置加载映射文件 2.3.2 配置数据库连 ...

  9. “AS3.0高级动画编程”学习:第三章等角投影(上)

    什么是等角投影(isometric)? 原作者:菩提树下的杨过出处:http://yjmyzz.cnblogs.com 刚接触这个概念时,我也很茫然,百度+google了N天后,找到了一些文章: [转 ...

  10. tomcat/Java指定加载jar包的路径

    背景:部署的web站点,应用默认加载工程的/webapps/工程名/WEB-INF/lib下的jar包   但是我需要提供一个和web工程没关系的的jar包管理目录   解决方法: 执行java方法时 ...