cropper头像上传

花了几天时间学习了下javaWeb中cropper头像上传功能,下面仅在此记录一下.

看到有篇博客是在前台用canvas的toDataURL处理,但貌似无法处理gif图像(如有大侠知道如何处理,还望告知),故放弃这一做法而选择在后端处理,

前台提交scaleX,scaleY,rotate,x,y,width,height及原始图片,后台缩放、旋转、裁剪.


一、下面先提供一个处理类:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.imageio.ImageIO;

import org.apache.commons.lang3.StringUtils;

import com.lzw.gif.AnimatedGifEncoder;
import com.lzw.gif.GifDecoder;

/**
 * @author nc-wl001 配合cropper头像上传插件使用的bean、(注意cropper插件缩放后图片大小是不变的及图片中心是不变的)
 */
public class Cropper {
	//水平方向缩放比例
	private double scaleX;
	//垂直方向缩放比例
	private double scaleY;
	// 旋转角度15、45等
	private int rotate;
	// 注意以下数据均是相对于旋转后的图片
	// 裁剪框左上角x
	private int x;
	// 裁剪框左上角y
	private int y;
	// 裁剪框宽度
	private int width;
	// 裁剪框高度
	private int height;
	// 为防止目标图片过大而添加的一个缩放比例参数
	protected double scale = 1.0;

	public Cropper(double scaleX,double scaleY,int rotate, int x, int y, int width, int height) {
		super();
		this.scaleX=scaleX;
		this.scaleY=scaleY;
		this.rotate = rotate;
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;
	}

	public int getRotate() {
		return rotate;
	}

	public void setRotate(int rotate) {
		this.rotate = rotate;
	}

	public int getX() {
		return x;
	}

	public void setX(int x) {
		this.x = x;
	}

	public int getY() {
		return y;
	}

	public void setY(int y) {
		this.y = y;
	}

	public int getWidth() {
		return width;
	}

	public void setWidth(int width) {
		this.width = width;
	}

	public int getHeight() {
		return height;
	}

	public void setHeight(int height) {
		this.height = height;
	}

	public double getScaleX() {
		return scaleX;
	}

	public void setScaleX(double scaleX) {
		this.scaleX = scaleX;
	}

	public double getScaleY() {
		return scaleY;
	}

	public void setScaleY(double scaleY) {
		this.scaleY = scaleY;
	}

	/**
	 * 设置缩放比例
	 * 
	 * @param scale
	 */
	protected void setScale(double scale) {
		this.x = (int) Math.ceil(this.x * scale / this.scale);
		this.y = (int) Math.ceil(this.y * scale / this.scale);
		this.width = (int) Math.ceil(this.width * scale / this.scale);
		this.height = (int) Math.ceil(this.height * scale / this.scale);
		this.scale = scale;
	}

	/**
	 * 设置目标图片宽度
	 * 
	 * @param width
	 */
	public void setFactWidth(int width) {
		double scale = width * this.scale / this.width;
		this.setScale(scale);
	}

	/**
	 * 设置目标图片高度
	 * 
	 * @param height
	 */
	public void setFactHeight(int height) {
		double scale = height * this.scale / this.height;
		this.setScale(scale);
	}

	/**
	 * 获取源图片旋转后的目标容器大小
	 * 
	 * @param source
	 *            源图片大小
	 * @return
	 */
	protected final Dimension getTargetDimension(Dimension source) {
		source.setSize(source.getWidth() * this.scale, source.getHeight()
				* this.scale);
		// 获取旋转后图片大小
		double theta = Math.toRadians(this.rotate);
		double target_width, target_height;
		if ((int) Math.floor(this.rotate / 90.0) % 2 == 0) {// 也即Math.sin(2*theta)>0(Math.sin(2*theta)=0时两个分支结果一样)
			target_width = Math.abs(source.width * Math.cos(theta)
					+ source.height * Math.sin(theta));
			target_height = Math.abs(source.height * Math.cos(theta)
					+ source.width * Math.sin(theta));
		} else {
			target_width = Math.abs(source.width * Math.cos(theta)
					- source.height * Math.sin(theta));
			target_height = Math.abs(source.height * Math.cos(theta)
					- source.width * Math.sin(theta));
		}
		Dimension target = new Dimension();
		target.setSize(target_width, target_height);
		return target;
	}

	/**
	 * 获取旋转并裁剪后的图片
	 * 
	 * @param src
	 *            源图片
	 * @return 旋转并裁剪后的图片
	 */
	protected final BufferedImage rotate_CropImage(BufferedImage src) {
		// 原图大小
		Dimension source = new Dimension(src.getWidth(), src.getHeight());
		// 旋转后大小
		Dimension target = this.getTargetDimension(source);
		// 旋转并裁剪后的目标图片
		BufferedImage rotate_crop_Image = new BufferedImage(this.width,
				this.height, BufferedImage.TYPE_INT_RGB);
		Graphics2D rotate_crop_g2d = rotate_crop_Image.createGraphics();
		// 设置填充色为白色
		rotate_crop_g2d.setColor(Color.WHITE);
		rotate_crop_g2d.fillRect(0, 0, this.width, this.height);
		// transform;translate(x,y)是将坐标系往右平移x,往下平移y,(x,y的正负对应正反方向)
		rotate_crop_g2d.translate((target.width - source.width) / 2 - this.x,
				(target.height - source.height) / 2 - this.y);// 平移坐标系(以便后面将原图画上去)
		// rotate 旋转坐标系
		rotate_crop_g2d.rotate(Math.toRadians(this.rotate), source.width / 2,
				source.height / 2);// 旋转坐标系、注意旋转中心
		rotate_crop_g2d.translate((1-this.scaleX)*source.width/2, (1-this.scaleY)*source.height/2);
		// 将图画上去
		rotate_crop_g2d.drawImage(src,AffineTransform.getScaleInstance(this.scale*this.scaleX, this.scale*this.scaleY), null);// 画图
		rotate_crop_g2d.dispose();
		return rotate_crop_Image;
		
	}

	/**
	 * 获取旋转并裁剪后的图片二进制流
	 * 
	 * @param inputStream
	 *            图片文件输入流
	 * @param imageType
	 *            图片文件类型(jpeg,png,jpg,gif等)
	 * @return 旋转并裁剪后的图片二进制流
	 */
	public byte[] rotate_CropImage(InputStream inputStream, String imageType) {
		byte[] result = new byte[] {};
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		try {
			if (StringUtils.equalsIgnoreCase(imageType, "gif")) {

				/*----------------AnimatedGifEncoder.java处理----------------------*/
				GifDecoder decoder = new GifDecoder();
				decoder.read(inputStream);
				AnimatedGifEncoder encoder = new AnimatedGifEncoder();
				encoder.start(outputStream);// 若要生成gif图片则使用encoder.start("xxx.gif");
				encoder.setRepeat(decoder.getLoopCount());
				for (int i = 0, n = decoder.getFrameCount(); i < n; i++) {
					encoder.setDelay(decoder.getDelay(i));
					encoder.addFrame(this.rotate_CropImage(decoder.getFrame(i)));
				}
				encoder.finish();

			} else {// 其他格式图片文件处理
				ImageIO.write(this.rotate_CropImage(ImageIO.read(inputStream)),
						imageType, outputStream);
			}
			result = outputStream.toByteArray();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (outputStream != null) {
				try {
					outputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return result;
	}

}


说明:此类是基于Cropper插件的几个裁剪数据而建的,可处理动态图,动态图处理部分是在网上找的几个处理gif的类.

这里打成了jar包方便使用:http://download.csdn.net/download/xinshijimanon/9796051

此类的重点是后三个方法,由于本人表达能力先天欠缺难过,就不详细熬叙此方法了(主要是获取旋转后的图片大小,以及在scaleX,scaleY不为1时如何将坐标系平移、旋转到正确位置)。

二、前台部分代码:

我这里是以弹窗的形式弹出修改头像框的,用的是layer插件,请酌情替换.

1、html/jsp

<!-- 修改头像2 -->
		<div id="editPhotoDialog" class="dialog">
		
			<label class="btn btn-primary btn-upload" for="inputImage" style="margin: 15px;">
				<input type="file" class="sr-only" id="inputImage" accept=".jpg,.jpeg,.png,.gif,.bmp">
				<span class="docs-tooltip" data-toggle="tooltip" data-animation="false" title="上传图片文件(.jpg、.jpeg、.png、.gif、.bmp)">
					<span class="icon icon-upload"> 选择图片</span>
				</span>
			</label>
			
			<div>
				<div class="col-md-8">
			        <!-- <h3>Demo:</h3> -->
			        <div class="img-container" style="width:390px;height:390px;">
			        	<img id="image" src="">
			        </div>
			    </div>
			    <div class="col-md-3">
			        <!-- <h3>Preview:</h3> -->
			        <div class="docs-preview clearfix">
			          <div class="img-preview preview-lg"></div>
			          <div class="img-preview preview-md"></div>
			          <div class="img-preview preview-sm"></div>
			          <div class="img-preview preview-xs"></div>
			        </div>
			    </div>
		    </div>
		    
		    
		    <div>
			    <div class="col-md-9 docs-buttons">
			    	<div class="btn-group">
			          	<button class="btn btn-primary" data-method="zoom" data-option="0.1" type="button" title="Zoom In">
			           		<span class="docs-tooltip" data-toggle="tooltip" title="放大">
			              		<span class="icon icon-zoom-in"></span>
			            	</span>
			          	</button>
			          	<button class="btn btn-primary" data-method="zoom" data-option="-0.1" type="button" title="Zoom Out">
			            	<span class="docs-tooltip" data-toggle="tooltip" title="缩小">
			              		<span class="icon icon-zoom-out"></span>
			            	</span>
			          	</button>
			          	<button class="btn btn-primary" data-method="rotate" data-option="-45" type="button" title="Rotate Left">
			            	<span class="docs-tooltip" data-toggle="tooltip" title="左旋转45度">
			              		<span class="icon icon-rotate-left"></span>
			            	</span>
			          	</button>
			          	<button class="btn btn-primary" data-method="rotate" data-option="45" type="button" title="Rotate Right">
			            	<span class="docs-tooltip" data-toggle="tooltip" title="右旋转45度">
			              		<span class="icon icon-rotate-right"></span>
			            	</span>
			          	</button>
			        </div>
			        
			        <div class="btn-group">
				          <button type="button" class="btn btn-primary" data-method="move" data-option="-2" data-second-option="0" title="Move Left">
				            <span class="docs-tooltip" data-toggle="tooltip" data-animation="false" title="左移">
				              <span class="icon icon-arrow-left"></span>
				            </span>
				          </button>
				          <button type="button" class="btn btn-primary" data-method="move" data-option="2" data-second-option="0" title="Move Right">
				            <span class="docs-tooltip" data-toggle="tooltip" data-animation="false" title="右移">
				              <span class="icon icon-arrow-right"></span>
				            </span>
				          </button>
				          <button type="button" class="btn btn-primary" data-method="move" data-option="0" data-second-option="-2" title="Move Up">
				            <span class="docs-tooltip" data-toggle="tooltip" data-animation="false" title="上移">
				              <span class="icon icon-arrow-up"></span>
				            </span>
				          </button>
				          <button type="button" class="btn btn-primary" data-method="move" data-option="0" data-second-option="2" title="Move Down">
				            <span class="docs-tooltip" data-toggle="tooltip" data-animation="false" title="下移">
				              <span class="icon icon-arrow-down"></span>
				            </span>
				          </button>
			       </div>
			       
			       <div class="btn-group">
			          <button type="button" class="btn btn-primary" data-method="addScaleX" data-option="0.08" data-second-option="2" data-three-option="3" title="Flip Horizontal">
			            <span class="docs-tooltip" data-toggle="tooltip" data-animation="false" title="水平放大">
			              <span class="icon  icon-chevron-left"></span>
			            </span>
			          </button>
			          <button type="button" class="btn btn-primary" data-method="addScaleX" data-option="-0.08" data-second-option="2" data-three-option="3" title="Flip Horizontal">
			            <span class="docs-tooltip" data-toggle="tooltip" data-animation="false" title="水平缩小">
			              <span class="icon icon-chevron-right"></span>
			            </span>
			          </button>
			          <button type="button" class="btn btn-primary" data-method="addScaleY" data-option="0.08" data-second-option="2" data-three-option="3" title="Flip Vertical">
			            <span class="docs-tooltip" data-toggle="tooltip" data-animation="false" title="竖直放大">
			              <span class="icon  icon-chevron-up"></span>
			            </span>
			          </button>
			          <button type="button" class="btn btn-primary" data-method="addScaleY" data-option="-0.08" data-second-option="2" data-three-option="3" title="Flip Vertical">
			            <span class="docs-tooltip" data-toggle="tooltip" data-animation="false" title="竖直缩小">
			              <span class="icon icon-chevron-down"></span>
			            </span>
			          </button>
			       </div>
			    </div>
		    </div>

		    
		</div>

说明:可以看到最后一组按钮中有data-Method="addScaleX"或data-Method="addScaleY",这需要修改cropper.js的源码,添加addScaleX,addScaleY方法.

在cropper.js中找到scaleX,scaleY方法,在其后面分别添加这两个方法(分别用于在水平、垂直方向拉伸原图):

 /**
   * add scaleX of the image
   *
   * @param {Number} addNumber
   * 
   * @param {Number} decimalCount
   * 
   * @param {Number} maxABS
   * 
   */
  addScaleX:function addScaleX(addNumber,decimalCount,maxABS){
	  var self = this;
	  var scaleX = self.image.scaleX;
	  scaleX+=addNumber;
	  if(decimalCount&&!isNaN(decimalCount)){
		  scaleX=scaleX.toFixed(parseInt(decimalCount));
	  }
	  if(scaleX>Math.abs(maxABS)||scaleX<-Math.abs(maxABS)){
		  return;
	  }
	  this.scaleX(scaleX);
  },


  /**
   * add scaleY of the image
   *
   * @param {Number} addNumber
   * 
   * @param {Number} decimalCount
   * 
   * @param {Number} maxABS
   * 
   */
  addScaleY:function addScaleY(addNumber,decimalCount,maxABS){
	  var self = this;
	  var scaleY = self.image.scaleY;
	  scaleY+=addNumber;
	  if(decimalCount&&!isNaN(decimalCount)){
		  scaleY=scaleY.toFixed(parseInt(decimalCount));
	  }
	  if(scaleY>Math.abs(maxABS)||scaleY<-Math.abs(maxABS)){
		  return;
	  }
	  this.scaleY(scaleY);
  },

2、js

	$("a.editPhoto").on("click",function(){
		layer.closeAll();
		layer.open({
			type : 1,
			title : false,
			closeBtn : 1,
			shift : 2,
			shade : 0.01,
			shadeClose : false,
			area : ['600px','600px'],
			content : $("#editPhotoDialog"),
			btn : [ '确认', '取消' ],
			yes : function(index) { // 确认按钮
				if($("#image").attr("src")==""){
					layer.msg("未选择图片文件", {
						time : 1000
					});
					return;
				}

				var cropperData=$("#image").cropper("getData");
				var fd=new FormData();
				fd.append("file",$("#image").data("file"));				
				fd.append("scaleX",cropperData.scaleX);
				fd.append("scaleY",cropperData.scaleY);
				fd.append("rotate",parseInt(cropperData.rotate));
				fd.append("x",parseInt(cropperData.x));
				fd.append("y",parseInt(cropperData.y));
				fd.append("width",parseInt(cropperData.width));
				fd.append("height",parseInt(cropperData.height));
				var loadIndex=0;
				$.ajax({
					url : "../../user/webSetPersonalPhoto",//设置个人头像接口
					type: 'post',
					data: fd,
					dataType: 'json',
					cache: false,
					processData: false,
					contentType: false,
					beforeSend: function(){
						layer.close(index);
						loadIndex = layer.load(1);
					},
					success:function(data, status, xhr, $form) {
						layer.msg(data.msg, {
							time : 1000
						});
						$("#header .userinfo div").css({//刷新个人头像
							"background-image" : "url(../../user/getPersonalPhoto?_="+ new Date().getTime()+ ")"
						});
					},
					complete : function(xhr, status, $form) {
						layer.close(loadIndex);
						if (status == "parsererror") {
							location.reload(true);// 刷新浏览器,类似于按F5
						}
					}
				});

			},
			end : function(index) {
				$("#image").cropper("destroy").attr("src","").data("file",null);
			}
		});
	});
	
	$(".btn-group").on('click', '[data-method]', function () {
		
		var $this = $(this);
	    var data = $this.data();
	    var $target;
	    var result;
 
	    if ($this.prop('disabled') || $this.hasClass('disabled')) {
	      return;
	    }

	    if ($("#image").data('cropper') && data.method) {
	      data = $.extend({}, data); // Clone a new one
	      if (typeof data.target !== 'undefined') {
	        $target = $(data.target);
	        if (typeof data.option === 'undefined') {
	          try {
	            data.option = JSON.parse($target.val());
	          } catch (e) {
	            console.log(e.message);
	          }
	        }
	      }
	      if (data.method === 'rotate') {
	    	  $("#image").cropper('clear');
	      }
	      if(data.method === 'addScaleX'||data.method === 'addScaleY'){
	    	  result = $("#image").cropper(data.method, data.option, data.secondOption,data.threeOption);
	      }else{
	    	  result = $("#image").cropper(data.method, data.option, data.secondOption);
	      }
	      
	      if (data.method === 'rotate') {
	    	  $("#image").cropper('crop');
	      }
	      if ($.isPlainObject(result) && $target) {
	        try {
	          $target.val(JSON.stringify(result));
	        } catch (e) {
	          console.log(e.message);
	        }
	      }

	    }
		
	});
	
	var URL = window.URL || window.webkitURL;
	var uploadedImageURL;
	if (URL) {
		$('#inputImage').change(function (e) {
			var files = this.files;
		    var file;
		    if (files && files.length) {
		    	file = files[0];
		    	var errorMsg="";
		    	if (!/^image\/\w+$/.test(file.type)) {
		    		errorMsg="请选择图片文件";
		        }else if(file.size==0){
		        	errorMsg="图片为空,请重新选择";
	    		}else if(file.size>2048000){
	    			errorMsg="图片大小超过最大限制(2MB)";
	    		}
		    	if(errorMsg!=""){
		    		$("#inputImage").val("");
		    		layer.msg(errorMsg,{time:1000});
		    		return;
		    	}
	    		if (uploadedImageURL) {
	    			URL.revokeObjectURL(uploadedImageURL);
	    		}
	    		uploadedImageURL = URL.createObjectURL(file);
	    		$("#image").cropper("destroy").attr('src', uploadedImageURL).data('file',file).cropper({
					aspectRatio: 1 / 1,
					dragMode:"move",
					toggleDragModeOnDblclick:false,
					strict:true,
					minCanvasWidth: 50,//画布
					minCanvasHeight: 0,
					minCropBoxWidth: 50,//裁剪框
					minCropBoxHeight: 0,
					minContainerWidth: 50,//容器
					minContainerHeight: 50,
					/*viewMode:2,*/
				    preview: '.img-preview',
				    crop: function (e) {
				    	
				    }
				});
	    		$("#inputImage").val("");
		    }
		});
	} else {
		$('#inputImage').prop('disabled', true).parent().addClass('disabled');
	}

说明:以上代码中使用的cropper版本是Cropper v3.0.0-beta,下载地址:https://github.com/fengyuanchen/cropper

js部分使用的是FormData提交文件即裁剪旋转数据(因input file选择文件时再单击'取消'(而不是'打开')后file就没有了,造成不好的体验).

3、css

/* Basic */


/* Main
 * ========================================================================== */


/* Icons
 * -------------------------------------------------------------------------- */

.icon {
  display: inline-block;
  width: 20px;
  height: 20px;
  background-image: url("../assets/img/icons.png");
  vertical-align: middle;
}

.icon-move {
  background-position: 0 0;
}

.icon-crop {
  background-position: -30px 0;
}

.icon-zoom-in {
  background-position: -60px 0;
}

.icon-zoom-out {
  background-position: -90px 0;
}

.icon-rotate-left {
  background-position: -120px 0;
}

.icon-rotate-right {
  background-position: -150px 0;
}

.icon-lock {
  background-position: -180px 0;
}

.icon-unlock {
  background-position: -210px 0;
}

.icon-remove {
  background-position: -240px 0;
}

.icon-refresh {
  background-position: -270px 0;
}

.icon-upload {
  background-position: -300px 0;
}

.icon-off {
  background-position: -330px 0;
}

.icon-info {
  background-position: -360px 0;
}


/* Alerts
 * -------------------------------------------------------------------------- */

.docs-alert {
  display: none;
  position: fixed;
  top: 20px;
  left: 0;
  right: 0;
  height: 0;
  text-align: center;
  opacity: 0.9;
}

.docs-alert .message {
  display: inline-block;
  padding: 5px 10px;
  border-radius: 2px;
  background-color: #aaa;
  color: #fff;
}

.docs-alert .primary {
  background-color: #0074d9;
}

.docs-alert .success {
  background-color: #2ecc40;
}

.docs-alert .info {
  background-color: #39cccc;
}

.docs-alert .warning {
  background-color: #ff851b;
}

.docs-alert .danger {
  background-color: #ff4136;
}

/* Button
 * -------------------------------------------------------------------------- */

.btn-primary {
  border-color: #003973; /* hsb(210, 100%, 45%) */
  background-color: #00468c; /* hsb(210, 100%, 55%) */
}

.btn-primary:hover,
.btn-primary:focus,
.btn-primary:active,
.btn-primary.active,
.btn-primary.active:focus,
.btn-primary.active:hover {
  border-color: #00264d; /* hsb(208, 100%, 10%) */
  background-color: #003366; /* hsb(208, 100%, 40%) */
}


/* Basic style
 * -------------------------------------------------------------------------- */

body {
  overflow-x: hidden;
}


/* Header */

.docs-header {
  border-color: #003973;
  background-color: #00468c;
  color: #fff;
}

.docs-header .navbar-brand {
  color: #eee;
}

.docs-header .navbar-toggle {
  border-color: #003973;
  background-color: #00468c;
}

.docs-header .navbar-toggle:hover,
.docs-header .navbar-toggle:focus {
  border-color: #003366;
  background-color: #003973;
}

.docs-header .navbar-collapse {
  border-color: #003973;
}

.docs-header .navbar-text {
  color: #ddd;
}

.docs-header .navbar-nav > li > a {
  color: #eee;
}


/* Content */

.img-container,
.img-preview {
  background-color: #f7f7f7;
  overflow: hidden;
  width: 100%;
  text-align: center;
}

.img-container {
  min-height: 200px;
  max-height: 390px;
  margin-bottom: 20px;
}

@media (min-width: 768px) {
  .img-container {
    min-height: 390px;
  }
}

.img-container > img {
  max-width: 100%;
}

.docs-preview {
  margin-left: 15px;
  margin-bottom: 10px;
}

.img-preview {
  float: left;
  margin-right: 10px;
  margin-bottom: 10px;
  border: 1px solid grey;
}

.img-preview > img {
  max-width: 100%;
}

.preview-lg {
  width: 148px !important;
  height: 148px !important;
}

.preview-md {
  width: 78px !important;
  height: 78px !important;
}

.preview-sm {
  width: 39px !important;
  height: 39px !important;
}

.preview-xs {
  width: 25px !important;
  height: 25px !important;
  margin-right: 0;
}

.docs-data > .input-group {
  margin-bottom: 10px;
}

.docs-data > .input-group > label {
  min-width: 80px;
}

.docs-data > .input-group > span {
  min-width: 50px;
}

.docs-buttons > .btn,
.docs-buttons > .btn-group,
.docs-buttons > .form-control {
  margin-right: 5px;
  margin-bottom: 10px;
}

.docs-toggles > .btn,
.docs-toggles > .btn-group,
.docs-toggles > .dropdown {
  margin-bottom: 10px;
}

.docs-tooltip {
  display: block;
  margin: -6px -12px;
  padding: 6px 12px;
}

.docs-tooltip > .icon {
  margin: 0 -3px;
  vertical-align: middle;
}

.tooltip-inner {
  white-space: normal;
}

.btn-upload .tooltip-inner {
  white-space: nowrap;
}

@media (max-width: 400px) {
  .btn-group-crop {
    margin-right: -15px!important;
  }

  .btn-group-crop > .btn {
    padding-left: 5px;
    padding-right: 5px;
  }

  .btn-group-crop .docs-tooltip {
    margin-left: -5px;
    margin-right: -5px;
    padding-left: 5px;
    padding-right: 5px;
  }
}

.docs-options .dropdown-menu {
  width: 100%;
}

.docs-options .dropdown-menu > li {
  padding: 3px 20px;
}

.docs-options .dropdown-menu > li:hover {
  background-color: #f7f7f7;
}

.docs-options .dropdown-menu > li > label {
  display: block;
}

.docs-cropped .modal-body {
  text-align: center;
}

.docs-cropped .modal-body > img,
.docs-cropped .modal-body > canvas {
  max-width: 100%;
}


三、后台部分代码:

1、后台用的是spring的MultipartFile、Spring这部分的配置大致如下:

<!-- file uploader support -->
	<bean id="multipartResolver"
		class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
		<property name="defaultEncoding" value="UTF-8" />
		<property name="maxUploadSize" value="255541538" />
	</bean>
2、后台接口:(这里图片是以二进制流的形式存在数据库中的、请酌情修改)

/** 设置个人头像
	 * @param scaleX 裁剪参数scaleX
	 * @param scaleY 裁剪参数scaleY
	 * @param rotate 裁剪参数rotate
	 * @param x 裁剪参数x
	 * @param y 裁剪参数y
	 * @param width 裁剪参数width
	 * @param height 裁剪参数height
	 * @param file 图片文件
	 * @return
	 */
	@RequestMapping(value = "/webSetPersonalPhoto", method = RequestMethod.POST)
	@ResponseBody
	public Tidings<User> webSetPersonalPhoto(
			@RequestParam("scaleX") Double scaleX,
			@RequestParam("scaleY") Double scaleY,
			@RequestParam("rotate") Integer rotate,
			@RequestParam("x") Integer x,
			@RequestParam("y") Integer y,
			@RequestParam("width") Integer width,
			@RequestParam("height") Integer height,
			@RequestParam("file") MultipartFile file) {
		String fileName=file.getOriginalFilename().toLowerCase();
		String imageType=fileName.substring(fileName.lastIndexOf(".")+1);
		if (!fileName.matches(".*\\.(jpg|jpeg|png|gif|bmp)")) {
			return new Tidings<User>("failure", "请选择图片文件");
		}
		//初始化裁剪信息
		Cropper cropper=new Cropper(scaleX,scaleY,rotate, x, y, width, height);
		//设置目标图片宽度
		cropper.setFactWidth(512);
		
		Subject subject = SecurityUtils.getSubject();
		String username = (String) subject.getPrincipal();
		InputStream input;
		byte[] photo = null;
		try {
			input = file.getInputStream();
			photo=cropper.rotate_CropImage(input, imageType);
			input.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		User user=userServiceImpl.findByUsername(username);
		if (photo != null && this.userServiceImpl.updatePropByUsername(username, "personalPhoto", photo)) {
			return new Tidings<User>("success", "设置个人头像成功",user);
		} else {
			return new Tidings<User>("failure", "设置个人头像失败",user);
		}
	}


上面的SecurityUtils、Subject是Shiro权限框架中的东西,Tidings是封装返回结果的类.请酌情修改部分代码.

附:

1、Tiddings.java:

public class Tidings<T> {
	private String status;
	private String msg;
	private T t;
	
	public Tidings(){
		super();
	}

	public Tidings(String status,String msg){
		super();
		this.status=status;
		this.msg=msg;
	}
	
	public Tidings(String status,String msg,T t){
		super();
		this.status=status;
		this.msg=msg;
		this.t=t;
	}


	public String getStatus() {
		return status;
	}

	public void setStatus(String status) {
		this.status = status;
	}

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}

	public T getT() {
		return t;
	}

	public void setT(T t) {
		this.t = t;
	}

}
2、获取个人头像:

/**
	 * 获取个人头像
	 * 
	 * @param request
	 * @param response
	 */
	@RequestMapping(value = "/getPersonalPhoto", method = RequestMethod.GET)
	public void getPersonalPhoto(HttpServletRequest request, HttpServletResponse response) {
		Subject subject = SecurityUtils.getSubject();
		String username = (String) subject.getPrincipal();
		User user = this.userServiceImpl.findByUsername(username);
		byte[] b = user.getPersonalPhoto();
		if (b == null) {//未设置图片则用一张默认图片
			FileInputStream fis = null;
			try {
				fis = new FileInputStream(
						request.getSession().getServletContext().getRealPath("/") + "img/default_personalPhoto.png");
				b = new byte[fis.available()];
				fis.read(b);
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				if (fis != null) {
					try {
						fis.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
		response.setContentType("image/png");
		OutputStream out = null;
		try {
			out = response.getOutputStream();
			out.write(b);
			out.flush();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (out != null) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}


相关推荐
©️2020 CSDN 皮肤主题: 技术工厂 设计师:CSDN官方博客 返回首页