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"
onerror="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"
onerror="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>