基于jquery的imgAreaSelect.js插件+JAVA后台实现图片裁剪保存功能

前段时间,项目在做个人信息设置,其中有一项是设置用户头像信息,需要将用户选择的头像按照用户需要进行剪切,同时保存为大(120*120)、中(75*75)、小(35*35)三种格式的图像,分别显示到不同的位置。

需求很简单,就是这么easy,这个其中重点就是用户可以自己对选择的头像进行截取,最终选择了基于jquery的imgareaselect.js。既然插件都有了,那就开工吧!

第一步: 前端下载必须的js插件,后台使用java自带imageio包处理,不需要其他jar包。 jquery.imgareaselect-0.9.10.zip jquery.js

第二步: 新建静态页面index.html (页面有点丑哈。。。)

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>jquery.imgareaselect图像区域剪切</title>
    <meta id="i18n_pagename" content="index-common">
    <meta name="viewport" content="width=device-width">
    <meta name="keywords" content="" />
    <meta name="description" content=""/>
    <link rel="stylesheet" href="css/imgareaselect-default.css">
    <link rel="stylesheet" href="css/index.css">
</head>
<body>
    <div>
        <input type="file" style="display: none" id="upImg" onchange="changeImg(this)">
        <label for="upImg" id="preview">
            <img id="imghead" src="images/normal.png" width="198" height="198" alt="头像">
        </label>
    </div>

    <div class="boxFooter">
        <input type="hidden" name="x1" value="0">
        <input type="hidden" name="y1" value="0">
        <input type="hidden" name="x2" value="100">
        <input type="hidden" name="y2" value="100"> 
        <button name="confirm" id="subPhoto" >确&nbsp;定</button>
        <div id="imgmsg"></div>
    </div>
    <script src="js/jquery.js"></script>        
    <!-- 加载js文件 -->
    <script src="js/jquery.imgareaselect.min.js"></script>
    <script src="js/image.js"></script>
</body>
</html>

说明:该页面是模拟前端截取图像,并将起始位置坐标和图片base64编码发送给后端,后端进行处理。

第三步: 新建前端处理js文件image.js

$(document).ready(function () { 
  //提交图片剪切信息到后台
  $("#subPhoto").click(function(){
      var x1 = $("input[name='x1']").val();
      var y1 = $("input[name='y1']").val();
      var x2 = $("input[name='x2']").val();
      var y2 = $("input[name='y2']").val();
      var img64 = $("#imghead").attr("src");
      alert(x1+":"+y1+":"+x2+":"+y2);
      var url = "";
      var param = {
        'x1': x1,
        'y1': y1,
        'x2': x2,
        'y2': y2,
        'image': img64
      }
      $.post(url,param,function(data){
        alert(data);
      });
  })

}); 

//点击图像区域选择图片
function changeImg(obj){
  //图片选择处理
  var file = obj;
  var MAXWIDTH  = 198; 
  var MAXHEIGHT = 198;
  var MAXSIZE = 2048*1024;
  var div = document.getElementById('preview');
  if (file.files && file.files[0]){
    if (file.files[0].size > MAXSIZE) {
      alert("more than " + (MAXSIZE/1024/1024) + "M");
      return false;
    };
    div.innerHTML ='<img id=imghead>';
    var img = document.getElementById('imghead');
    img.onload = function(){
       var rect = clacImgZoomParam(MAXWIDTH, MAXHEIGHT, img.offsetWidth, img.offsetHeight);
       img.width  =  rect.width;
       img.height =  rect.height;
       img.style.marginTop = rect.top+'px';
    }
    var reader = new FileReader();
    reader.onload = function(evt){
      img.src = evt.target.result;
    }
    reader.readAsDataURL(file.files[0]);
  }

  //图片剪切区域处理
  $('#imghead').imgAreaSelect({ 
      x1:0, 
      y1:0, 
      x2:100, 
      y2:100, 
      aspectRatio: '1:1', //比例
      handles: true, 
      onSelectChange: function(img, selection){//图片剪切区域变化时触发
        $("#imgmsg").html("x1:"+selection.x1+", y1:"+selection.y1+", x2:"+selection.x2+", y2:"+selection.y2);
      }, 
      onSelectEnd: function (img, selection) {//图片剪切区域结束时触发
        $('input[name="x1"]').val(selection.x1);
        $('input[name="y1"]').val(selection.y1);
        $('input[name="x2"]').val(selection.x2);
        $('input[name="y2"]').val(selection.y2);
     }
  }); 
}

//设置图片显示区域为固定大小,方便后台按统一比例截取图片
function clacImgZoomParam( maxWidth, maxHeight, width, height ){
   var param = {top:0, left:0, width:width, height:height};
   if( width>maxWidth || height>maxHeight ){
     rateWidth = width / maxWidth;
     rateHeight = height / maxHeight;
     if( rateWidth > rateHeight ){
         param.width =  maxWidth;
         param.height = Math.round(height / rateWidth);
     }else {
         param.width = Math.round(width / rateHeight);
         param.height = maxHeight;
     }
   }
   param.left = Math.round((maxWidth - param.width) / 2);
   param.top = Math.round((maxHeight - param.height) / 2);
   return param;
}

说明:该js文件不难,大家都应该能看懂,调用imgAreaSelect.js的地方在$(‘#imghead’).imgAreaSelect({…})里面,这块也不难看懂,在 onSelectChange: function(img, selection){})这个地方图片剪切区域变化时触发,我们可以做一些其他的处理,比如预览截取部分的图像等等。clacImgZoomParam()方法主要是用于对用户选择的图像进行限定固定高度和宽度,方便后台按照统一的比例计算截取的坐标位置。

第四步: 后台接收并处理图片,后台是基于SpringMVC的,不熟悉的可以自学下下。 UserController.java

package com.gochina.tc.api;

import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.gochina.tc.po.ResultPo;
import com.gochina.tc.service.UserService;

/**
 * 用户信息处理
 * @author hwy
 *
 */
@Controller
@RequestMapping(value = "/user")
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping(value = "/uploadImage",method = RequestMethod.POST)
    public ResultPo uploadUserImage(
            @RequestParam(value = "image",required = true) String base64Code,
            @RequestParam(value = "x1",required = true) int x1,
            @RequestParam(value = "y1",required = true) int y1,
            @RequestParam(value = "x2",required = true) int x2,
            @RequestParam(value = "y2",required = true) int y2){
        String result = userService.uploadUserImage(base64Code, x1, y1, x2, y2);
        if(result.equals("")){
            return new ResultPo(false, "上传用户图像失败");
        }else{
            return new ResultPo("success", result);
        }
    }
}

UserService.java

package com.gochina.tc.service;

/**
 * 用户信息处理service
 * @author hwy
 *
 */
public interface UserService {
    /**
     * 上传并处理用户图片
     */
    public String uploadUserImage(String base64Code,int x1,int y1,int x2,int y2);
}

UserServiceImpl.java

package com.gochina.tc.service.impl;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

import org.springframework.stereotype.Service;

import com.gochina.tc.util.ImageUtil;

/**
 * 用户信息处理serviceImpl
 * @author hwy
 *
 */
@Service
public class UserServiceImpl implements UserService {

    /**
     * 用户上传图片处理
     */
    public String uploadUserImage(String base64Code,int x1,int y1,int x2,int y2){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        String dateStr = sdf.format(new Date());
        String path = "D:/user_image/" + dateStr + "/";
        String url = "http://127.0.0.1:8080/user_image/" + dateStr + "/";
        File f = new File(path);
        if(!f.exists()){
            f.mkdirs();
        }
        String fileName = UUID.randomUUID().toString().replaceAll("-", "")+".jpg";
        String temp_fileName = path + "t_" + fileName;
        String b_fileName = path + "b_" + fileName;
        String m_fileName = path + "m_" + fileName;
        String s_fileName = path + "s_" + fileName;
        String result = "";
        try{
            //创建原始文件(先强制修改为jpg格式)
            boolean isCreate = ImageUtil.base64ToImage(base64Code, path + fileName);
            if(isCreate){
                float scale = ImageUtil.getScaleCutImage(path + fileName);//比例
                int width = (int) ((x2-x1)*scale);
                int height = width;
                int start_x = (int) (x1*scale);
                int start_y = (int) (y1*scale);
                //剪切图片
                ImageUtil.cutImage(path + fileName, temp_fileName, start_x, start_y, width, height);
                //剪切以后的图片压缩到固定大小的图片
                ImageUtil.reduceImageByWidthHeight(temp_fileName, b_fileName, 200, 200);//200*200大图
                ImageUtil.reduceImageByWidthHeight(temp_fileName, m_fileName, 120, 120);//120*120中图
                ImageUtil.reduceImageByWidthHeight(temp_fileName, s_fileName, 70, 70);//70*70小图
                result = url + fileName;
            }
        }catch(Exception e){
            e.printStackTrace();
        }
        return result;
    }

}

说明:该文件处理流程大致为,1、创建原始文件,将base64编码转换成图片,并强制修改为jpg格式。2、获取图片的压缩比例,并计算剪切图片的长宽和起始坐标。3、按照要求剪切图片。4、将剪切以后的图片压缩到固定大小的图片。

ImageUtil.java

package com.gochina.tc.util;

import java.awt.Color;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;

import sun.misc.BASE64Decoder;

import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGEncodeParam;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
/**
 * 上传的图片进行截取,压缩处理
 * @author hwy
 *
 */
public class ImageUtil {

     /**
     * 长高等比例缩小图片
     * @param srcImagePath 读取图片路径
     * @param toImagePath 写入图片路径
     * @param ratio 缩小比例
     * @throws IOException
     */
    public static void reduceImageByRatio(String srcImagePath,String toImagePath,float ratio) throws IOException{
        FileOutputStream out = null;
        try{
            //读入文件  
            File file = new File(srcImagePath);  
            // 构造Image对象  
            BufferedImage src = javax.imageio.ImageIO.read(file);  
            int width = src.getWidth();  
            int height = src.getHeight();  
            // 缩小边长 
            BufferedImage tag = new BufferedImage((int)(width / ratio), (int)(height / ratio), BufferedImage.TYPE_INT_RGB);  
            // 绘制 缩小后的图片 
            tag.getGraphics().drawImage(src, 0, 0, (int)(width / ratio), (int)(height / ratio), null);  
            out = new FileOutputStream(toImagePath);  
            JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out); 
            JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(tag);
            param.setQuality(0.75f, true);// 默认0.75 
            encoder.setJPEGEncodeParam(param);
            encoder.encode(tag);
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            if(out != null){
                out.close();  
            }
            out = null;
            System.gc();
        }
    }

    /**
     * 缩小图片到固定长高
     * @param srcImagePath 读取图片路径
     * @param toImagePath 写入图片路径
     * @param width 缩小后图片宽度
     * @param height 缩小后图片长度
     * @throws IOException
     */
    public static void reduceImageByWidthHeight(String srcImagePath, String toImagePath, int width, int height) throws IOException{
        FileOutputStream out = null;
        try{
            //读入文件  
            File file = new File(srcImagePath);  
            // 构造Image对象 
            BufferedImage src = javax.imageio.ImageIO.read(file);  
            // 缩小边长 
            BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);  
            // 绘制缩小后的图片 
            tag.getGraphics().drawImage(src, 0, 0, width, height, null);  
            out = new FileOutputStream(toImagePath);  
            JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out); 
            JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(tag);
            param.setQuality(1f, true);// 默认0.75 
            encoder.setJPEGEncodeParam(param);
            encoder.encode(tag);
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            if(out != null){
                out.close();  
            }
            out = null;
            System.gc();
        }
    }
    /**
     * 剪切图片
     * @param srcpath
     * @param subpath
     * @param x
     * @param y
     * @param width
     * @param height
     * @throws IOException
     */
    public static void cutImage(String srcpath,String subpath,int x,int y,int width,int height) throws IOException {  
        FileInputStream is = null;  
        ImageInputStream iis = null;  
        try {  
            // 读取图片文件  
            is = new FileInputStream(srcpath);  
            Iterator<ImageReader> it = ImageIO.getImageReadersByFormatName("jpg");  
            ImageReader reader = it.next();  
            // 获取图片流  
            iis = ImageIO.createImageInputStream(is);  
            reader.setInput(iis, true);  
            ImageReadParam param = reader.getDefaultReadParam();  
            // 图片裁剪区域。Rectangle 指定了坐标空间中的一个区域,通过 Rectangle 对象的左上顶点的坐标(x,y)、宽度和高度可以定义这个区域。 
            Rectangle rect = new Rectangle(x, y, width, height);  
            // 提供一个 BufferedImage,将其用作解码像素数据的目标。  
            param.setSourceRegion(rect);  
            BufferedImage bi = reader.read(0, param);  
            // 保存新图片  
            ImageIO.write(bi, "jpg", new File(subpath));  
        }finally {  
            if(is != null){
                is.close(); 
            }
            if(iis != null){  
                iis.close();
            }
        }  
    }  

    /**
     * 获取图片缩放尺寸
     * @param srcImagePath
     * @return
     * @throws IOException
     */
    public static float getScaleCutImage(String srcImagePath) throws IOException{
        //读入文件  
        File file = new File(srcImagePath);  
        // 构造Image对象  
        BufferedImage src = javax.imageio.ImageIO.read(file);  
        int width = src.getWidth();  
        int height = src.getHeight();
        float scale = 0f;
        if(width >= height){
            scale = (float)width/200;
        }else if(width < height){
            scale = (float)height/200;
        }
        return scale;
    }

    /**
     * @Descriptionmap 对字节数组字符串进行Base64解码并生成图片
     * @param base64 图片Base64数据
     * @param path 图片路径
     * @return
     */
    public static boolean base64ToImage(String base64, String path) {
        // 图像数据为空
        if(base64 == null){
            return false;
        }
        if(base64.indexOf("base64") != -1){
            base64 = base64.substring(base64.indexOf("base64")+7, base64.length());
        }
        // Base64解码
        BASE64Decoder decoder = new BASE64Decoder();
        try{
            byte[] bytes = decoder.decodeBuffer(base64);
            for(int i=0;i<bytes.length;i++){
                // 调整异常数据
                if(bytes[i] < 0){
                    bytes[i] += 256;
                }
            }
            // 生成jpeg图片
            OutputStream out = new FileOutputStream(path);
            out.write(bytes);
            out.flush();
            out.close();
            //非jpg格式的图片强转为jpg
            if(base64.indexOf("image/jpeg") == -1){
                imageToJPG(path, path);
            }
            return true;
        }catch(Exception e){
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将图片格式转成jpg的支持( GIF->JPG GIF->PNG PNG->GIF(X) PNG->JPG)
     * @param src1
     * @param result
     * @throws IOException
     */
    public static void imageToJPG(String src1,String result) throws IOException{
        File f = new File(src1);  
        f.canRead();  
        BufferedImage src = ImageIO.read(f);
        ImageIO.write(src, "jpg", new File(result));
    }

}

说明:这个工具类注释写的比较详细,也不难看懂。有几个地方需要注意下: 1、imageToJPG()方法,将图片格式转成jpg的暂时只支持( GIF->JPG 、GIF->PNG 、PNG->GIF(X)、 PNG->JPG)这几种形式。 2、base64ToImage()方法,对字节数组字符串进行Base64解码并生成图片,里面前端传过来的数据串会带有类似data:image/png;base64,这段标示,我们需要将这部分截取掉才可以生成图片。 3、getScaleCutImage()方法,计算scale的时候,基数为200,这个要跟前端的图片显示区域保持一致,不然截取的图片就不正确了。 4、reduceImageByRatio()方法,长高等比例缩小图片中, param.setQuality(0.75f, true); 设置图片的质量,这个默认图像质量是0.75,如果想高质量保存,就设置为接近1即可,当然图片容量大小就会跟着变化了。

好了,大功即将告成!先来看下页面的效果如何吧!

当然这个效果比较low,不是很好看,仅仅是个demo而已,再来贴个项目线上的效果图吧,这个不low哦!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏青玉伏案

iOS开发之各种动画各种页面切面效果

注:其中有些效果调用了CATransition的Private API, 仅供娱乐。 补充:还是有好多小伙伴问那些可以在AppStore中使用,调用私有API的...

19210
来自专栏机器之心

教程 | 如何在Python中快速进行语料库搜索:近似最近邻算法

3054
来自专栏.Net /C#

C# 绘制PDF嵌套表格

嵌套表格,即在一张表格中的特定单元格中再插入一个或者多个表格,使用嵌套表格的优点在于能够让内容的布局更加合理,同时也方便程序套用。下面的示例中,将介绍如何通过C...

130
来自专栏生信技能树

学IGV必看的初级教程

Integrative Genomics Viewer (IGV)作为一个高性能的可视化工具,可以交互式的察看综合的基因组相关数据,也友好的支持多种数据类型,自...

65512
来自专栏林德熙的博客

WPF 可获得焦点属性

本文来告诉大家 WPF 的可获得焦点属性,如果希望一个元素可以获得键盘输入,那么就需要一个元素是可以获得焦点,而且焦点就在元素上。

642
来自专栏hightopo

扩展HT for Web之HTML5表格组件的Renderer和Editor

1723
来自专栏木子昭的博客

让css3动画变得有趣wowjs

animate.css 包含了一组炫酷、有趣、跨浏览器的动画,可以在你的项目中直接使用。

823
来自专栏CRPER折腾记

Vue 折腾记 - (6) 写一个不大靠谱的backToTop组件

返回顶部这个功能用jq实现,好容易实现,一个animate配合scrollTo就搞定了

651
来自专栏程序员互动联盟

【专业技术】搜狗歌词窗口如何来实现

大家都见过以前Sogou歌词窗口的样子吧,感觉是歌词的字体直接贴在windows桌面上一样,但是还可以用鼠标控制,这个是怎么做成的呢?其实我也不知道^_^,估计...

34910
来自专栏菩提树下的杨过

silverlight:如何在图片上挖个洞?

一、不写代码的方法:用Blend 看图说话: 这是待处理的图片win7 ? 在win7上,画一个矩形,再用钢笔随便画个封闭的path ? 将矩形与path合...

17710

扫码关注云+社区