JAVA + js 实现 头像上传及裁剪功能

题记

我想大家对头像上传功能在陌生不过了把,每个人都应该用过,当然,从不更改头像的大侠除外,写到这里,我突然想到我的CSDN用户也没有头像,于是,我到设置里面准备去看看CSDN的头像上传功能是怎样的,可是我弄了半天都没搞定,提示说高版本浏览器不支持预览,难道只有IE6才支持?难得吐槽一下,不知道是我人品问题还是没用对。如图:



准备工作

好了,废话不多说了,一般常用的头像上传有两种(据我所知):

普通的文件上传

普通文件上传莫非就是,给你一个选择文件选择按钮,你选择好文件后,剩下的事情你就不管了,后台会把你上传的图片按一定的比例缩放,或者无任何处理,或其他操作,这种方式的好处坏处显而易见,前后台的实现相对来说比较简单,可是用户体验就不是这么满意了,我以前也遇到过这种,不但自己需要找到大概合适大小比例的图片,有事还得自己修改裁剪,然后再传上去,最后结果并不会让我满意,我不得不重新在去做一遍这些繁琐的操作!

带预览裁剪功能的上传:

现在很多用户体验还不错的网站或软件都采用这种方法,最常用的实现就是用的flash,用户上传一张图片,马上就可以自己选取需要的部分,而且还带有及时的预览效果,体验非常不错。


步入正题,最近项目需要,有个地方需要用户上传头像,本来按照产品设计,直接一个选择框,用户选择图片后上传就搞定!这种方法不说用户,就连我开发人员都觉得真心难用,于是我决定弄一个可以预览裁剪上传头像的功能,下面是用到的一款js的图片裁剪插件,性能还算行几乎兼容所有的浏览器。


Jcrop  http://deepliquid.com/content/Jcrop.html


实现原理

当我没接触到这个功能之前,以为这有多神秘多神秘,其实就是两个原图的img标签,其中一个是让你选择预览范围,一个让你看到你选择后的效果,利用Draggable和Resizable特性在原图上创建出一个选择框,然后再按照你选择的范围按照一定的比例和算法让效果图那个img只显示你选择的那一部分,最后把你最后选择的范围坐标以及大小传给后台,后台按照这个范围裁剪相应的图片即可!


具体用法

首先引入相应的js/css文件





然后写入一个image标签,


最后给img标签添加可裁剪功能


效果如下:



如果想预览你选择后的图片,再写入一个img标签


代码改成

$(function(){

	$('#target').Jcrop({
		onChange: showPreview,
		onSelect: showPreview,
		aspectRatio: 1
	});

});
当选择框改变,将会执行下面的函数

function showPreview(coords)
{
	var rx = 100 / coords.w;
	var ry = 100 / coords.h;

	$('#preview').css({
		width: Math.round(rx * 300) + 'px',
		height: Math.round(ry * 300) + 'px',
		marginLeft: '-' + Math.round(rx * coords.x) + 'px',
		marginTop: '-' + Math.round(ry * coords.y) + 'px'
	});
}

这样,id为preview将只会显示你选择的范围的图片




与JAVA后台结合

前台实现其实很简单,Jcrop下载下来已经有完整的Demo可以运行,用法也很简单,最麻烦的就是后台了,我们可以通过回调函数showPreview得到选择框的x,y,width,height,然后传到后台按照坐标裁剪图片即可,官方是这种说的,可问题来了,我们得到的x,y,w,h只是相对于被缩小后的id为target的img标签的,假设一张1024x768的图,target的大小肯定是固定的,图中为504x374,所以得到的x,y,h,w是基于此比例来的,如果传到后台你用这个坐标来裁剪原图,肯定有问题,这个问题纠结了我很久,我想过通过一定的比例算法来计算实际的x,y是多少,但一直都没找到传到后台的坐标和实际要裁剪的坐标有什么关系,我网上查了很多人说也是直接通过传过来的x,y等参数直接生成新的图片,可生成的图片完全和我选择的不一样,我也想过把图片先压缩成target一样大,然后xy就正确了,但是看到preview里的效果图了吗,那是在原图上选择后的效果,后来我发现preview不就是我要的效果吗,于是打开firebug研究了一下




通过样式我们看到,图片被放大到758x561,并且左外边距和上外边距分别设置成了-529和-261,于是我明白了,后台按照一样的思路不就行了吗?先把图片转成758x561,然后在按照x:528 y:261的坐标裁剪一定的大小,不就是最后要的图片么,


后台用到的java代码方法

 /**
     *  缩放后裁剪图片方法
     * @param srcImageFile 源文件
     * @param x  x坐标
     * @param y  y坐标
     * @param destWidth 最终生成的图片宽
     * @param destHeight 最终生成的图片高
     * @param finalWidth  缩放宽度
     * @param finalHeight  缩放高度
     */
    public static void abscut(String srcImageFile, int x, int y, int destWidth,
                              int destHeight,int finalWidth,int finalHeight) {
        try {
            Image img;
            ImageFilter cropFilter;
            // 读取源图像
            BufferedImage bi = ImageIO.read(new File(srcImageFile));
            int srcWidth = bi.getWidth(); // 源图宽度
            int srcHeight = bi.getHeight(); // 源图高度

            if (srcWidth >= destWidth && srcHeight >= destHeight) {
                Image image = bi.getScaledInstance(finalWidth, finalHeight,Image.SCALE_DEFAULT);//获取缩放后的图片大小
                cropFilter = new CropImageFilter(x, y, destWidth, destHeight);
                img = Toolkit.getDefaultToolkit().createImage(
                        new FilteredImageSource(image.getSource(), cropFilter));
                BufferedImage tag = new BufferedImage(destWidth, destHeight,
                        BufferedImage.TYPE_INT_RGB);
                Graphics g = tag.getGraphics();
                g.drawImage(img, 0, 0, null); // 绘制截取后的图
                g.dispose();
                // 输出为文件
                ImageIO.write(tag, "JPEG", new File(srcImageFile));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }




你可能感兴趣的:(JAVA)