layim之修改群组头像

我们基于Croppe插件,实现HTML5裁剪图片并上传功能,完美整合到layim中,先来看看效果图
在这里插入图片描述直入主题,呈上教程

一. 添加右键菜单

绑定入口,添加右键菜单(如图),前几天已经整理,专机带你一程✈layim整合右键菜单
在这里插入图片描述

二. 绑定右击菜单点击事件

var $ = layui.jquery, active = {
    
    
	menuUpdate: function(){
    
    
	    /*修改群图标*/
		var groupId = $(this).parent().data('id');
		layer.open({
    
    
			type: 2,
			maxmin: !0,
			title: '更改群图标',
			area: ['998px', '556px'],
			shade: !1,
			offset: 'auto',
			skin: "layui-box",
			anim: 2,
			id: "layui-layim-chatlog",
			content: 'avatar.html?groupId='+groupId	// 更改群图标页面
		});
    }
};
$('body').on('click', '.layui-layer-tips li', function(e){
    
    
    var type = $(this).data('type');
    active[type] ? active[type].call(this) : '';
	// 清空所有右击弹框
    emptyTips();
});

三. 创建改群图标页面

附上源码,点击下载

3.1、插入css样式:

<link href="/cropper/css/cropper.min.css" rel="stylesheet" />
<link href="/cropper/css/sitelogo.css" rel="stylesheet" />
<link href="/cropper/css/bootstrap.min.css" rel="stylesheet" />

3.2、引用js插件:

<script src="/cropper/js/cropper.min.js" type="text/javascript"></script>
<script src="/cropper/js/sitelogo.js" type="text/javascript"></script>
<script src="/cropper/js/bootstrap.min.js" type="text/javascript"></script>

3.3、添加html代码:

<div id="avatar-modal" style="width:100%;">
	<div class="modal-content" style="border:none;">
		<form id="AvatarForm" class="avatar-form" enctype="multipart/form-data" method="post">
			<div class="modal-body">
				<div class="avatar-body">
					<div class="avatar-upload">
						<input id="groupId" name="groupId" value="${groupId }" type="hidden">
						<input class="avatar-src" id="avatar_src" name="avatar_src" type="hidden">
						<input class="avatar-data" id="avatar_data" name="avatar_data" type="hidden">
						<label for="avatarInput">图片上传</label>
						<input class="avatar-input" id="itemFile" name="itemFile" type="file">
					</div>
					<div class="row">
						<div class="col-md-9">
							<div class="avatar-wrapper"></div>
						</div>
						<div class="col-md-3">
							<div class="avatar-preview preview-lg"></div>
							<div class="avatar-preview preview-md"></div>
							<div class="avatar-preview preview-sm"></div>
						</div>
					</div>
					<div class="row avatar-btns">
						<div class="col-md-9">
							<div class="btn-group">
								<button class="btn" data-method="rotate" data-option="-90" type="button" title="Rotate -90 degrees"><i class="fa fa-undo"></i> 向左旋转</button>
							</div>
							<div class="btn-group">
								<button class="btn" data-method="rotate" data-option="90" type="button" title="Rotate 90 degrees"><i class="fa fa-repeat"></i> 向右旋转</button>
							</div>
						</div>
						<div class="col-md-3">
							<button class="btn btn-success btn-block avatar-save uploadAvatar" type="button"><i class="fa fa-save"></i> 保存修改</button>
						</div>
					</div>
				</div>
			</div>
		</form>
	</div>
</div>

3.4、绑定修改头像事件:

// 修改头像
$("body").delegate(".uploadAvatar","click",function() {
    
    
	var fileName = $("#itemFile").val();
	if(fileName==null){
    
    
		layer.msg('请选择上传的图片!');
       	return;
	}
	var index = fileName.lastIndexOf(".");
	var suffix = fileName.substring(index).toLowerCase();
	if(suffix!=".bmp"&&suffix!=".png"&&suffix!=".gif"&&suffix!=".jpg"&&suffix!=".jpeg"&&suffix!=".pic"){
    
    
		layer.msg('请选择图片!');
       	return;
	}
	
	var loading = layer.load(0, {
    
    shade: [0.2, '#fff']});
	var action = "chat/group/uploadAvatar";		// 服务器地址
	var form = new FormData(document.getElementById("AvatarForm"));
	$.ajax({
    
    
        url: action,
        type: "post",
        data: form,
        processData: false,
        contentType: false,
		dataType : 'json',
        success: function(msg){
    
    
        	layer.close(loading);
        	// 成功后的代码
        },
        error: function(e){
    
    
        	layer.close(loading);
        	layer.msg('操作失败,请稍后再试!');
        }
    });
});

3.5、清除缓存数据:
修改图标返回成功之后,我们需要更新缓存数据,防止再次引用该群组时图标没更新。

// 更新缓存
var cacheGroup = parent.layui.layim.cache().group;
$.each(cacheGroup, function (i, item) {
    
    
   	if(item && item.id==revertGroup.id) {
    
    
   		item.avatar = revertGroup.avatar;
   	}
});
parent.layui.layim.cache().group = cacheGroup;

3.5、修改主面板群组图标:
因为我们以open方式打开iframe窗体,所以要修改主面板群聊图标,必须获取父级页面才能找到聊天主面板的元素,我们F12可以看到,主面板每个群组的样式都是以layim-group开头加群组编号,因此我们修改群组头像就简单了。

// 修改主面板群聊图标
parent.$(".layim-list-group .layim-group'"+groupId+"'] img").attr('src',groupAvatar);
layer.msg("修改群图标成功!");

其中,groupId为群组的编号,groupAvatar为保存到服务器的图标地址。

3.6、关闭窗体:
最后,为了提高用户体验,我们需要自动关闭修改群组图标的窗体。那么问题来了,我们在哪里关闭,是在打开窗体的时候回调关闭呢,还是在修改图标成功后直接关闭。通过网上寻找资料,原来Layui内置了一个getFrameIndex方法,此方法一般用于在Iframe页关闭自身时用的,代码如下。

// 1秒后关闭本自身窗体
setTimeout(function(){
    
    
		// 先得到当前Iframe层的索引
		var index = parent.layer.getFrameIndex(window.name);
		// 再执行关闭
		parent.layer.close(index);
	},1000);

简单两行代码,就实现了我们一开始觉得无从下手的功能,有时候敢于面对,奇迹便会发生!

四. 服务器代码

4.1、图片旋转工具类:

package com.cn.unit.img;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;

import javax.imageio.ImageIO;

/**
 * 图片旋转工具类
 * @author [email protected] 
 * 2016-02-12
 */
public class ImageRotateUtil {
    
    
	
	// ===源图片路径名称如:D:\1.jpg
	private String srcpath;
	
	// ===剪切图片存放路径名称.如:D:\2.jpg
	private String subpath;
	
	// ===新图类型 只支持gif,jpg,png
	private String suffix;
	
	// ===旋转角度
	private int degree;
    
    public ImageRotateUtil(String srcpath, String subpath, String suffix, int degree) {
    
    
        this.srcpath = srcpath;
        this.subpath = subpath;
        this.suffix = suffix;
        this.degree = degree;
    }
	
	/**
     * 旋转
     */
    public void spin() throws Exception {
    
    
        int swidth = 0; // 旋转后的宽度
        int sheight = 0; // 旋转后的高度
        int x; // 原点横坐标
        int y; // 原点纵坐标

        File file = new File(srcpath);
        if (!file.isFile()) {
    
    
            throw new Exception("ImageDeal>>>" + file + " 不是一个图片文件!");
        }
        BufferedImage bi = ImageIO.read(file); // 读取该图片
        // 处理角度--确定旋转弧度
        degree = degree % 360;
        if (degree < 0)
            degree = 360 + degree;// 将角度转换到0-360度之间
        double theta = Math.toRadians(degree);// 将角度转为弧度
        
        // 确定旋转后的宽和高
        if (degree == 180 || degree == 0 || degree == 360) {
    
    
            swidth = bi.getWidth();
            sheight = bi.getHeight();
        } else if (degree == 90 || degree == 270) {
    
    
            sheight = bi.getWidth();
            swidth = bi.getHeight();
        } else {
    
    
            swidth = (int) (Math.sqrt(bi.getWidth() * bi.getWidth()
                    + bi.getHeight() * bi.getHeight()));
            sheight = (int) (Math.sqrt(bi.getWidth() * bi.getWidth()
                    + bi.getHeight() * bi.getHeight()));
        }
        
        x = (swidth / 2) - (bi.getWidth() / 2);// 确定原点坐标
        y = (sheight / 2) - (bi.getHeight() / 2);
        
        BufferedImage spinImage = new BufferedImage(swidth, sheight, bi.getType());
        // 设置图片背景颜色
        Graphics2D gs = (Graphics2D) spinImage.getGraphics();
        gs.setColor(Color.white);
        gs.fillRect(0, 0, swidth, sheight);// 以给定颜色绘制旋转后图片的背景
        
        AffineTransform at = new AffineTransform();
        at.rotate(theta, swidth / 2, sheight / 2);// 旋转图象
        at.translate(x, y);
        AffineTransformOp op = new AffineTransformOp(at, AffineTransformOp.TYPE_BICUBIC);
        spinImage = op.filter(bi, spinImage);
        File sf = new File(subpath);
        ImageIO.write(spinImage, suffix, sf); // 保存图片
    }

	public String getSrcpath() {
    
    
		return srcpath;
	}
	public void setSrcpath(String srcpath) {
    
    
		this.srcpath = srcpath;
	}
	public String getSubpath() {
    
    
		return subpath;
	}
	public void setSubpath(String subpath) {
    
    
		this.subpath = subpath;
	}
	public String getSuffix() {
    
    
		return suffix;
	}
	public void setSuffix(String suffix) {
    
    
		this.suffix = suffix;
	}
	public int getDegree() {
    
    
		return degree;
	}
	public void setDegree(int degree) {
    
    
		this.degree = degree;
	}
}

4.2、图片裁剪工具类:

package com.cn.unit.img;

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

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

/**
 * 图片裁剪工具类
 * @author [email protected] 
 * 2016-02-12
 */
public class ImageOperateUtil {
    
    
	
	// ===源图片路径名称如:D:\1.jpg
	private String srcpath;
	// ===剪切图片存放路径名称.如:D:\2.jpg
	private String subpath;
	// ===剪切点x坐标
	private int x;
	// ===剪切点y坐标
	private int y;
	// ===剪切点宽度
	private int width;
	// ===剪切点高度
	private int height;

	public ImageOperateUtil() {
    
    };

	public ImageOperateUtil(String srcpath, String subpath, int x, int y, int width, int height) {
    
    
		// 判断图片高度宽度
		int[] exif = ImageExifUtil.getImgWH(srcpath);
		int w_ = exif[0];
		int h_ = exif[1];
		width = width >= w_ ? width : w_;
		height = height >= h_ ? height : h_;
		
		this.srcpath = srcpath;
		this.subpath = subpath;
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;
	}

	/**
	 * 对图片裁剪,并把裁剪完蛋新图片保存 。
	 */
	public void cut() throws IOException {
    
    

		FileInputStream is = null;
		ImageInputStream iis = null;

		try {
    
    
			// 读取图片文件
			is = new FileInputStream(srcpath);

			/*
			 * 返回包含所有当前已注册 ImageReader 的 Iterator,这些 ImageReader 声称能够解码指定格式。
			 * 参数:formatName - 包含非正式格式名称 .(例如 "jpeg" 或 "tiff")等 。
			 */
			Iterator<ImageReader> it = ImageIO.getImageReadersByFormatName("jpg");
			ImageReader reader = it.next();
			// 获取图片流
			iis = ImageIO.createImageInputStream(is);

			/*
			 * <p>iis:读取源.true:只向前搜索 </p>.将它标记为 ‘只向前搜索'。
			 * 此设置意味着包含在输入源中的图像将只按顺序读取,可能允许 reader 避免缓存包含与以前已经读取的图像关联的数据的那些输入部分。
			 */
			reader.setInput(iis, true);

			/*
			 * <p>描述如何对流进行解码的类<p>.用于指定如何在输入时从 Java Image I/O
			 * 框架的上下文中的流转换一幅图像或一组图像。用于特定图像格式的插件 将从其 ImageReader 实现的
			 * getDefaultReadParam 方法中返回 ImageReadParam 的实例。
			 */
			ImageReadParam param = reader.getDefaultReadParam();

			/*
			 * 图片裁剪区域。Rectangle 指定了坐标空间中的一个区域,通过 Rectangle 对象
			 * 的左上顶点的坐标(x,y)、宽度和高度可以定义这个区域。
			 */
			Rectangle rect = new Rectangle(x, y, width, height);

			// 提供一个 BufferedImage,将其用作解码像素数据的目标。
			param.setSourceRegion(rect);

			/*
			 * 使用所提供的 ImageReadParam 读取通过索引 imageIndex 指定的对象,并将 它作为一个完整的
			 * BufferedImage 返回。
			 */
			BufferedImage bi = reader.read(0, param);

			// 保存新图片
			ImageIO.write(bi, "jpg", new File(subpath));
		} finally {
    
    
			if (is != null)
				is.close();
			if (iis != null)
				iis.close();
		}
	}

	public int getHeight() {
    
    
		return height;
	}
	public void setHeight(int height) {
    
    
		this.height = height;
	}
	public String getSrcpath() {
    
    
		return srcpath;
	}
	public void setSrcpath(String srcpath) {
    
    
		this.srcpath = srcpath;
	}
	public String getSubpath() {
    
    
		return subpath;
	}
	public void setSubpath(String subpath) {
    
    
		this.subpath = subpath;
	}
	public int getWidth() {
    
    
		return width;
	}
	public void setWidth(int width) {
    
    
		this.width = width;
	}
	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;
	}
}

4.3、控制器方法:

/**
 * 修改图标
 */
@RequestMapping(value = "/uploadAvatar", method = RequestMethod.POST, produces = "text/html;charset=UTF-8")
   public @ResponseBody String uploadAvatar(@RequestParam("itemFile") MultipartFile itemFile, 
   		@RequestParam("groupId") String groupId, 
   		@RequestParam("avatar_src") String avatarSrc, 
   		@RequestParam("avatar_data") String avatarData, 
   		HttpServletRequest request, HttpServletResponse response) {
    
    
    //OPTIONS预请求缓存的有效时间 单位秒  
    response.setHeader("Access-Control-Max-Age", "3600");
    //允许自定义的请求头,多个用逗号分隔  
    response.setHeader("Access-Control-Allow-Headers", "content-type, x-requested-with");
    if (itemFile.isEmpty()) {
    
    
		return false;
    }
   	String avatar = null, suffix = null, fileName = null;
   	// 获取原文件名
   	String fileName = itemFile.getOriginalFilename();
   	// 获取文件后缀
   	String suffix = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length()).toLowerCase();
   	// 保存数据库文件地址
	String avatar = "ligentres/" + System.currentTimeMillis() + "." + suffix;
	// 系统根目录完整地址
	String rootPath = request.getSession().getServletContext().getRealPath("/");
    String tmpPath = rootPath + avatar;
    // 上传图标
    try {
    
    
        byte[] bytes = itemFile.getBytes();
        BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(new File(tmpPath)));
        stream.write(bytes);
        stream.close();
    } catch (IOException e) {
    
    
		return false;
    }
       
    // {"x":246.64,"y":1356.90,"height":2048.05,"width":2048.05,"rotate":90}
   	log.info("创建群聊/修改图标:avatarData["+avatarData+"]");
    // 裁剪头像
    if(avatarData!=null){
    
    
        try {
    
    
        	JSONObject jsonObject = new JSONObject(avatarData);
            if(!jsonObject.isNull("x") && !jsonObject.isNull("y") && !jsonObject.isNull("width") && !jsonObject.isNull("height")){
    
    
        		Integer x = null, y = null, width = null, height = null, size;
    			if(!jsonObject.isNull("rotate")){
    
    
    				Integer rotate = jsonObject.getInt("rotate");
    				if(rotate.intValue() != 0){
    
    
    					ImageRotateUtil imageRotate = new ImageRotateUtil(tmpPath, tmpPath, suffix, rotate);
						imageRotate.spin();
    				}
    			}
    			x = jsonObject.getInt("x");
    			y = jsonObject.getInt("y");
    			width = jsonObject.getInt("width");
    			height = jsonObject.getInt("height");
    			size = width >= height ? width : height;
    			ImageOperateUtil operateImage = new ImageOperateUtil(tmpPath, tmpPath, x, y, size, size);
    			operateImage.cut();
    		}
        }catch (Exception e) {
    
    
        	log.error("修改图标:裁剪图片发生异常"+e);
		}
    }
    // 这里填写你的代码 保存数据库
    // --
	return true;
}

到这里,已经完美整合了修改群组图标功能,小生不才,若文章和代码有表述不当之处,还请不吝赐教。

赠人玫瑰手留余香,若对您有所帮助,来 点个赞呗!

猜你喜欢

转载自blog.csdn.net/ii950606/article/details/106352558