Struts2 图片上传、缩放、剪切处理 第二节
1、技术目标:
- 对上传的图片进行缩放、剪切操作
提示:本文示例项目已提供下载
2、处理流程:
1)在影片列表页面(films.jsp)点"修改"进入影片修改页面,如图:
2)在影片修改页面(update.jsp)中双击图片,进入图片缩放、剪切处理页面(imgscissor.jsp),如图,
update.jsp效果图:
imgscissor.jsp效果图:
3)在imgscissor.jsp页面中设置图片的缩放宽度、高度,如图,
提示:影片"刀见笑"海报图片原有尺寸为92(宽)*130(高),压缩为60 * 100
压缩前效果图:
4)在imgscissor.jsp页面中使用jQuery图片剪切插件"imgAreaSelect"对图片进行剪切操作,如图,
剪切前效果图:
5)采用ajax方式将图片的缩放、剪切参数提交给Action进行处理
6)服务器Action根据页面设置的参数对图片进行如下处理:
- 采用java-image-scaling-0.8.5.jar提供的功能对图片进行缩放处理
- 采用javax.imageio以及java.awt提供的功能对图片进行剪切处理
7)用处理好的图片替换原有的图片,服务器Action返回处理结果,页面提示处理结果并显示处理后的图片,如图,
压缩后效果图:
剪切后效果图:
注意:第二步、第三步处理可同时进行
3、使用准备
3.1)站点根路径下创建js文件夹,导入如下js、css文件:
jquery.js 版本:v1.4.2
jquery.tooltip.css jQuery信息提示插件样式
jquery.tooltip.js jQuery信息提示插件
jquery.form.js jQuery表单插件
loading.gif 进度提示图片
jquery.loadmask.css jQuery窗口屏蔽插件样式
jquery.loadmask.js jQuery窗口屏蔽插件
3.2)导入jQuery插件imgareaselect
在js文件夹下创建文件夹imgareaselect,imgareaselect下再创建css文件夹,imgareaselect下导入如下文件:
jquery.imgareaselect.jsjQuery图片剪切插件
imgareaselect/css下导入如下文件(图片剪切插件所选样式与图片素材):
border-anim-h.gif
border-anim-v.gif
border-h.gif
border-v.gif
imgareaselect-animated.css
imgareaselect-default.css
imgareaselect-deprecated.css
提示:imgareaselect插件的详细信息可访问
http://odyniec.net/projects/imgareaselect/examples.html#fixed-aspect-ratio
3.3)导入相关的jar包
gson-1.5.jar将Java对象转换成JSON
java-image-scaling-0.8.5.jar图片压缩工具
提示:对gson的使用有兴趣的可参看
http://hotstrong.iteye.com/blog/1164379
4、给影片修改页面(update.jsp)加入如下两处代码
注意:图片的缩放、剪切操作将从update.jsp开始
4.1)导入相关js、css文件
<link rel="stylesheet" href="<%=basePath %>/js/jquery.tooltip.css" type="text/css"/>
<script type="text/javascript" src="<%=basePath %>/js/jquery.js"></script>
<script type="text/javascript" src="<%=basePath %>/js/imgscissor.js"></script>
<script type="text/javascript" src="<%=basePath %>/js/jquery.tooltip.js"></script>
提示:imgscissor.js将在后续创建
4.2)为img标签加入scissor类样式,如下:
<img src="<s:property value="imgurl" />" class="scissor" width="92" height="130"
οnerrοr="javascript:this.src='<%=basePath %>/images/error.gif'" /><br />
修改后的update.jsp如下:
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8" %>
<%@taglib uri="/struts-tags" prefix="s" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>修改影片</title>
<link rel="stylesheet" href="<%=basePath %>/js/jquery.tooltip.css" type="text/css"/>
<script type="text/javascript" src="<%=basePath %>/js/jquery.js"></script>
<script type="text/javascript" src="<%=basePath %>/js/imgscissor.js"></script>
<script type="text/javascript" src="<%=basePath %>/js/jquery.tooltip.js"></script>
</head>
<body>
<s:form action="/film/updateFilm" method="post" enctype="multipart/form-data">
<s:hidden name="id" />
影片名称:<s:textfield name="fname" /><br />
<%-- 处理原有图片 --%>
<s:if test='imgurl != null and imgurl != ""'>
<%-- 保存原有图片的信息在修改时提交 --%>
<s:hidden name="imgurl"></s:hidden>
<%-- 显示原有图片,onerror属性:在打不开图片时显示提示图片 --%>
<img src="<s:property value="imgurl" />" class="scissor" width="92" height="130"
οnerrοr="javascript:this.src='<%=basePath %>/images/error.gif'" /><br />
</s:if>
<%--文件选择框 --%>
影片海报:<s:file id="imgPhoto" name="imgPhoto"/><br />
<s:submit value=" 修改 "></s:submit>
</s:form>
</body>
</html>
5、在js文件夹下创建imgscissor.js,用于处理"双击图片"向Action提交请求并进入图片缩放、剪切页面,代码如下:
$(document).ready(function() { //获取JS文件当前路径并设置站点绝对路径 var CurrentJsPath = (function (){ var k = document.getElementsByTagName("script"); srcStr = k[0].getAttribute("src"); //截取出站点路径 srcStr = srcStr.substring(0, srcStr.indexOf("/js/")); return srcStr; })(); /* * 创建表单,该表单访问ImageScissorAction并将两个参数传过去 * 参数:proportion 裁剪比例(一般的处理要求等比例裁剪) * 参数:originPath 图片url路径 */ $('body').append('<form id="toScissrorForm" action="' + CurrentJsPath + 'film/toScissor" method="post">' + '<input type="hidden" name="proportion" value="" />' + '<input type="hidden" name="originPath" value="" /> </form>'); $('#toScissrorForm').hide(); //为样式为scissor的元素(img标签)加入处理 $('.scissor').each(function() { //获取标签的src属性值(图片的url)并加入一个参数(当前时间)以防止缓存 var imgPath = $(this).attr('src') + '?' + new Date().getTime(); //将带时间参数的url再设置给图片的src属性 $(this).attr('src',imgPath); //设置鼠标移到图片上的提示文字 $(this).tooltip({ showURL: false, bodyHandler: function() { return '双击裁剪图片'; } }); //设置图片的双击事件处理 $(this).dblclick(function(){ //获取img标签的src值(url路径) var imageSrc = $(this).attr('src'); //读取img的宽、高 var width = $(this).attr("width"); var height = $(this).attr("height"); //设置拖拽比例(裁剪图片应该按比例裁剪) var proportion = width + ":" + height; //将值设置给表单 $('#toScissrorForm input[name=originPath]').val(imageSrc); $('#toScissrorForm input[name=proportion]').val(proportion); $('#toScissrorForm').submit();//双击提交表单 }); }); });
6、在manager文件夹下创建jsp文件imgscissor.jsp(图片压缩、剪切操作页面),压缩、剪切操作完成后将参数异步发送给ImageScissorAction,代码如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path;
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>图片裁剪页面</title>
<!-- 导入插件样式 -->
<link rel="stylesheet"
href="<%=basePath %>/js/imgareaselect/css/imgareaselect-animated.css" type="text/css" />
<link rel="stylesheet" href="<%=basePath %>/js/jquery.tooltip.css" type="text/css"/>
<link href="<%=basePath %>/js/jquery.loadmask.css" rel="stylesheet" type="text/css" />
<!-- 设置图片操作DIV的样式 -->
<style type="text/css">
#imgDiv{
margin-left: 3px; margin-top: 5px; width: 800px; height: 700px;
overflow: auto;
scrollbar-3dlight-color:#595959;
scrollbar-arrow-color:#FFFFFF;
scrollbar-base-color:#CFCFCF;
scrollbar-darkshadow-color:#FFFFFF;
scrollbar-face-color:#CFCFCF;
scrollbar-highlight-color:#FFFFFF;
scrollbar-shadow-color:#595959;
}
</style>
<!-- 导入jQuery各种插件 -->
<script type="text/javascript" charset="UTF-8" src="<%=basePath %>/js/jquery.js"></script>
<script type="text/javascript" src="<%=basePath %>/js/imgareaselect/jquery.imgareaselect.js"></script>
<script type="text/javascript" src="<%=basePath %>/js/jquery.tooltip.js"></script>
<script type="text/javascript" src="<%=basePath %>/js/jquery.form.js"></script>
<script type="text/javascript" src="<%=basePath %>/js/jquery.loadmask.js"></script>
<script type="text/javascript">
$(document).ready(function() {
//获取拖拽比例
var proportion = $('#proportion').val();
//unescape可对通过escape()编码的字符串进行解码
var imagePath = unescape($('#img').attr('src'));
//图片的url设置给隐藏表单originPath,准备提交给Action
$('input[name=originPath]').val(imagePath);
//通过图片的url取出服务器域名全路径并设置好访问Action的url
var imgPath = $('#img').attr('src');
var newAction = imgPath.substring(0, imgPath.indexOf('/images')) +"/film/scissor";
//设置表单的action属性为Action的rul
$('#scissorForm').attr('action',newAction);
//为id为img的图片设置图片剪切插件
var imgArea = $('#img').imgAreaSelect({
fadeSpeed: 400,
handles: true,
instance: true,
aspectRatio: proportion, //设置拖拽比例
onSelectEnd : function(img, selection) {
$('#areaHeight').html((selection.y2 - selection.y1)+" 像素");
$('#areaWidth').html((selection.x2 - selection.x1)+" 像素");
$('input[name=x1]').val(selection.x1);
$('input[name=y1]').val(selection.y1);
$('input[name=x2]').val(selection.x2);
$('input[name=y2]').val(selection.y2);
$('#warning').hide();
}
});
//提交按钮的单击事件处理
$('#submitBtn').click(function(){
if(confirm("是否提交?")){
$('#scissorForm').submit();
}
});
//设置表单ajax异步提交
$('#scissorForm').submit(function() {
$(this).ajaxSubmit({
beforeSubmit: function(){//提交前的处理
//提交表单处理期间,屏蔽整个窗口
$('#content').mask("正在提交数据,请稍候。");
//关闭提交按钮
$('input[name=submit]').attr("disabled", true);
},
dataType: 'json',
success: function showResponse(responseText, statusText, xhr, $form){
//取消窗口屏蔽
$('#content').unmask();
$('#warning').show();
//打开提交按钮
$('input[name=submit]').attr("disabled", false);
imgArea.cancelSelection();
var imgPath = $('#img').attr('src')+'?'+new Date().getTime();
$('#img').attr('src',imgPath);
//重置参数
$('input[name=scaleHeight]').val('');
$('input[name=scaleWidth]').val('');
$('#areaHeight').html('');
$('#areaWidth').html('');
alert(responseText);
}
});
return false;
});
//返回上一页
$('#back').click(function(){
history.go(-1);
});
});
</script>
</head>
<body>
<div id="content" >
<!-- 保存图片修改后参数的表单 -->
<form id="scissorForm" method="post">
剪切区域,高:<label id="areaHeight"></label>
宽:<label id="areaWidth"></label>
<br /><br />
缩放宽:<input type="text" name="scaleWidth" value="" style="width: 100px;" />
缩放高:<input type="text" name="scaleHeight" value="" style="width: 100px;" />
<br /><br />
<!-- 提示信息 -->
<font color="red">[请先选取裁剪区域] [大尺寸图片可先缩放再剪切]</font>
<br /><br />
<input type="button" id="submitBtn" name="submitBtn" value="提交" />
<input type="button" id="back" value="返回" />
<%-- 图片剪切区域 --%>
<div id="imgDiv">
<!-- 待处理的图片 -->
<img id="img" src="<s:property value="originPath" />" class="scissor" alt="点击裁剪图片">
</div>
<!-- 保存图片操作参数的隐藏表单 -->
<input type="hidden" name="x1" value="" />
<input type="hidden" name="y1" value="" />
<input type="hidden" name="x2" value="" />
<input type="hidden" name="y2" value="" />
<input type="hidden" name="originPath" value="" />
</form>
</div>
<!-- 保存缩放比的隐藏表单 -->
<input type="hidden" id="proportion" name="proportion"
value="<s:property value="proportion" />" />
</body>
</html>
7、在com.xxx.util包下创建图片处理工具类ImageUtil,提供图片的压缩(scaleImage)、剪切(scissor)处理方法,代码如下:
package com.xxx.util;
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;
import com.mortennobel.imagescaling.ResampleOp;
/**
* 图片处理工具类
*
*/
public class ImageUtil {
/**
* 根据传入的图片坐标进行图片截取
*
* @param x1 X起点坐标
* @param x2 X终点坐标
* @param y1 Y起点坐标
* @param y2 Y终点坐标
* @param originPath 原始图片的存放路径
* @param savePath 截取后图片的存储路径
* @throws IOException
*/
public static void scissor(int x1, int x2, int y1, int y2,
String originPath, String savePath) throws IOException {
FileInputStream is = null;
ImageInputStream iis = null;
try {
// 读取图片文件
is = new FileInputStream(originPath);
/*
* 返回包含所有当前已注册 ImageReader 的 Iterator,
* 这些 ImageReader 声称能够解码指定格式。
* 参数:formatName - 包含非正式格式名称 .(例如 "jpeg" 或 "tiff")等 。
*/
Iterator<ImageReader> it = ImageIO
.getImageReadersByFormatName(getExtention(originPath)
.toLowerCase());
ImageReader reader = it.next();
// 获取图片流
iis = ImageIO.createImageInputStream(is);
/*
* iis:读取源.true:只向前搜索,将它标记为 ‘只向前搜索’。
* 此设置意味着包含在输入源中的图像将只按顺序读取,可能允许
* reader 避免缓存包含与以前已经读取的图像关联的数据的那些输入部分。
*/
reader.setInput(iis, true);
/*
* 描述如何对流进行解码的类,用于指定如何在输入时从 Java Image I/O
* 框架的上下文中的流转换一幅图像或一组图像。用于特定图像格式的插件
* 将从其 ImageReader 实现的
* getDefaultReadParam方法中返回 ImageReadParam 的实例。
*/
ImageReadParam param = reader.getDefaultReadParam();
/*
* 图片裁剪区域。Rectangle 指定了坐标空间中的一个区域,通过 Rectangle 对象
* 的左上顶点的坐标(x,y)、宽度和高度可以定义这个区域。
*/
Rectangle rect = new Rectangle(x1, y1, x2 - x1, y2 - y1);
// 提供一个 BufferedImage,将其用作解码像素数据的目标。
param.setSourceRegion(rect);
/*
* 使用所提供的 ImageReadParam 读取通过索引 imageIndex 指定的对象,并将 它作为一个完整的
* BufferedImage 返回。
*/
BufferedImage bi = reader.read(0, param);
// 保存新图片
ImageIO.write(bi, getExtention(originPath).toLowerCase(), new File(
savePath));
} finally {
if (is != null)
is.close();
if (iis != null)
iis.close();
}
}
/**
*
* 缩放图片
*
* @param width 宽
* @param height 高
* @param originPath 原始路径
* @param savePath 保存路径
* @throws IOException
*/
public static void scaleImage(int width, int height, String originPath,
String savePath) throws IOException {
BufferedImage sourceImage = readImage(originPath);
ResampleOp resampleOp = new ResampleOp(width, height);
BufferedImage rescaledTomato = resampleOp.filter(sourceImage, null);
ImageIO.write(rescaledTomato, getExtention(originPath).toLowerCase(),
new File(savePath));
}
private static BufferedImage readImage(String imagePath) throws IOException {
return readImage(new File(imagePath));
}
private static BufferedImage readImage(File image) throws IOException {
return ImageIO.read(image);
}
/**
* 功能:提取文件名的后缀
*
* @param fileName
* @return
*/
private static String getExtention(String fileName) {
int pos = fileName.lastIndexOf(".");
return fileName.substring(pos + 1);
}
}
8、在com.xxx.web.struts.action包下创建ImageScissorAction控制器,负责接收页面参数调用ImageUtil执行图片压缩、剪切操作,代码如下:
package com.xxx.web.struts.action;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.ServletActionContext;
import com.google.gson.Gson;
import com.opensymphony.xwork2.ActionSupport;
import com.xxx.util.ImageUtil;
public class ImageScissorAction extends ActionSupport {
private static final long serialVersionUID = -5971162241884111578L;
private Integer x1;
private Integer x2;
private Integer y1;
private Integer y2;
private String originPath;
private String savePath;
private Integer scaleWidth;
private Integer scaleHeight;
/**
* 缩放比例
*/
private String proportion;
/**
* 获取web服务器路径
* @param relativePath
* @return
*/
protected String getRealPath(String relativePath) {
return ServletActionContext.getServletContext().getRealPath(relativePath);
}
@Override
public String execute() throws Exception {
try {
// 取出服务器host以及端口等
originPath = originPath.substring(originPath.indexOf("/images"));
if (originPath.contains("?")) {
originPath = originPath.substring(0, originPath.indexOf("?"));
}
originPath = getRealPath(originPath);
if (savePath == null || savePath.trim().equals("")) {
savePath = originPath;
}
if (x1 != null && x2 != null && y1 != null && y2 != null) {
ImageUtil.scissor(x1, x2, y1, y2, originPath, savePath);
}
if (scaleWidth != null && scaleWidth != null) {
ImageUtil.scaleImage(scaleWidth, scaleHeight, originPath,
savePath);
}
outputJson("图片处理成功");
} catch (Exception e) {
outputJson("图片处理失败");
}
return null;
}
/**
* 直接转发imgscissor.jsp页面
* @return
*/
public String toScissor() {
return SUCCESS;
}
/**
* 输出JSON信息
* @param jsonObj
*/
private void outputJson(Object jsonObj){
HttpServletResponse response = ServletActionContext.getResponse();
response.setContentType("application/json;charset=utf-8");
response.setHeader("Cache-Control", "no-cache");
try {
PrintWriter pw = response.getWriter();
//将Java对象转换为JSON字符串
String gsonStr = new Gson().toJson(jsonObj);
//输出JSON字符串
pw.print(gsonStr);
pw.flush();
pw.close();
} catch (IOException e) {
System.out.println("输出GSON出错:" + e);
}
}
public void setX1(Integer x1) {
this.x1 = x1;
}
public void setX2(Integer x2) {
this.x2 = x2;
}
public void setY1(Integer y1) {
this.y1 = y1;
}
public void setY2(Integer y2) {
this.y2 = y2;
}
public void setOriginPath(String originPath) {
this.originPath = originPath;
}
public void setSavePath(String savePath) {
this.savePath = savePath;
}
public String getOriginPath() {
return originPath;
}
public void setScaleWidth(Integer scaleWidth) {
this.scaleWidth = scaleWidth;
}
public void setScaleHeight(Integer scaleHeight) {
this.scaleHeight = scaleHeight;
}
public String getProportion() {
return proportion;
}
public void setProportion(String proportion) {
this.proportion = proportion;
}
}
9、在applicationContext-actions.xml中配置ImageScissorAction,由Spring管理,配置如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <!-- 创建FilmAction --> <bean id="filmAction" class="com.xxx.web.struts.action.FilmAction" scope="prototype"/> <!-- 创建ImageScissorAction --> <bean id="imageScissorAction" class="com.xxx.web.struts.action.ImageScissorAction" scope="prototype"/> </beans>
10、在struts.xml中配置ImageScissorAction访问名称与方法的对应,配置如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="true" /> <constant name="struts.devMode" value="true" /> <constant name="struts.i18n.encoding" value="UTF-8" /> <constant name="struts.objectFactory" value="spring" /> <constant name="struts.objectFactory.spring.autoWire" value="type" /> <constant name="struts.ui.theme" value="simple"></constant> <package name="film" namespace="/film" extends="struts-default"> <!-- 获取所有影片 --> <action name="findFilm" class="filmAction" method="findFilm"> <result name="success">/manager/films.jsp</result> </action> <!-- 添加影片 --> <action name="insertFilm" class="filmAction" method="insertFilm"> <!-- 默认拦截器 --> <interceptor-ref name="defaultStack" /> <!-- 文件上传拦截器 --> <interceptor-ref name="fileUploadStack"> <!-- 配置允许上传的文件大小,单位字节(默认2M) --> <param name="maximumSize">10000000</param> <param name="allowedTypes"> image/bmp,image/png,image/gif,image/jpeg </param> </interceptor-ref> <result name="success" type="redirectAction">findFilm</result> </action> <!-- 获取影片详情 --> <action name="detailFilm" class="filmAction" method="detailFilm"> <result name="success">/manager/updateFilm.jsp</result> </action> <!-- 修改影片 --> <action name="updateFilm" class="filmAction" method="updateFilm"> <!-- 默认拦截器 --> <interceptor-ref name="defaultStack" /> <!-- 文件上传拦截器 --> <interceptor-ref name="fileUploadStack"> <!-- 配置允许上传的文件大小,单位字节(默认2M) --> <param name="maximumSize">10000000</param> <param name="allowedTypes"> image/bmp,image/png,image/gif,image/jpeg </param> </interceptor-ref> <result name="success" type="redirectAction">findFilm</result> </action> <!-- 删除影片 --> <action name="deleteFilm" class="filmAction" method="deleteFilm"> <result name="success" type="redirectAction">findFilm</result> </action> <!-- 图片剪切、压缩处理Action --> <action name="toScissor" class="imageScissorAction" method="toScissor"> <result name="success">/manager/imgscissor.jsp</result> </action> <action name="scissor" class="imageScissorAction"> <!-- 响应类型为text/html --> <param name="contentType">text/html</param> </action> </package> </struts>