Struts2实现图片上传(物业报修系统)

介绍(S & T)

“物业报修系统”项目里有个图片上传的功能模块,我花了近一个晚上理解原理和写出代码。项目框架是SSH2,而上传图片模块主要涉及到Struts2,所以我剥离Spring和Hibernate的内容,只放出Struts2的代码。

(N : 在2015年的时候,我在Android端写过一个类似的模块,但当时我的做法是调用Restful API将图片直接上传到服务器,写的跑在客户端上,而现在写的代码跑在服务器上,两者有较大的区别)

详细步骤(A)

主要步骤分别有Struts2配置文件的修改编写JSP代码编写后台Action处理

Struts2配置文件的修改

首先是web.xml,它是TomCat服务器的配置文件,为了使得网站的默认页面是上传照片页面(default_upload_pic.jsp),必须修改它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

<!-- 所有请求都通过FilterDispatcher查找actionMapper的设置来决定请求对应的是哪个Action -->

<display-name>邱永臣配置</display-name>

<!-- 过滤器的配置 -->
<filter>
<!-- 名字 -->
<filter-name>filterDispatcher</filter-name>
<!-- 过滤器对应的名字 -->
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>

<filter-mapping>
<filter-name>filterDispatcher</filter-name>
<!-- 过滤匹配的URL -->
<url-pattern>/*</url-pattern>
</filter-mapping>

<!-- 解决中文乱码问题 -->
<filter>
<filter-name>struts-clean</filter-name>
<filter-class>org.apache.struts2.dispatcher.ActionContextCleanUp</filter-class>
</filter>
<filter-mapping>
<filter-name>struts-clean</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<!-- 进入WEB应用访问的默认文件 -->
<welcome-file-list>
<welcome-file>default_upload_pic.jsp</welcome-file>
</welcome-file-list>

<context-param>
<param-name>contextConfigLocation</param-name>
<!-- 如果将该xml文件放在WEB-INF下面的话,就需要下面配置 <param-value>WEB-INF/applicationContext.xml</param-value> -->
<param-value>WEB-INF/applicationContext.xml</param-value>
<!-- 下面是默认配置 -->
<!-- <param-value>classpath:com/lzq/config/applicationContext-*.xml</param-value> -->
</context-param>

<!-- 采用Listener来初始化Spring容器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>

接着是struts.xml,它配置了Struts2。在一个页面被提交到服务器后,如何进行处理、由谁处理和处理之后给浏览者返回什么内容
等都由它决定。负责处理图片上传的Action是com.ilovecl.test包中的UploadPicAction,它决定浏览者跳转到page1.jsp或是page2.jsp。此外,其中还定义了文件过滤器,不允许浏览者上传过大的文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?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>

<package name="qiuyongchen" extends="struts-default">

<action name="uploadPic"
class="com.ilovecl.test.UploadPicAction">
<result name="success">/page1.jsp</result>
<result name="input">/page2.jsp</result>

<!-- 定义文件上传拦截器 -->
<interceptor-ref name="fileUpload">

<!-- 设置文件上传大小 -->
<param name="maximumSize">4096000</param>
</interceptor-ref>

<!-- 自定义了拦截器后必手动定义默认的拦截器,否则默认的拦截器不会被执行 -->
<interceptor-ref name="defaultStack"></interceptor-ref>

</action>

</package>
</struts>

JSP代码

JSP文件负责给浏览者显示内容,它提供一个文件选择框,浏览者选择图片后点击按钮便可上传(JSP是嵌入在HTML中的脚本,和javascript类似)。在代码里,我调用了uploadPic这个action,将图片传到服务器里后,就是它负责逻辑处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>

<%@ taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">

<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">

</head>

<body>

${requestScope.message}

<s:form action="uploadPic" theme="simple" enctype="multipart/form-data">
<table align="center" width="50%" border="1">
<tr>
<td>问题描述:</td>
<td><s:textfield name="problemDescription"></s:textfield></td>
</tr>
<tr>
<td>物业故障地点:</td>
<td><s:textfield name="place"></s:textfield></td>
</tr>
<tr>
<td>现场照片:</td>
<td id="more"><s:file name="file"></s:file></td>
</tr>
<tr>
<td><s:submit value=" submit "></s:submit></td>
<td><s:reset value=" reset "></s:reset></td>
</tr>
</table>
</s:form>
</body>

</html>

后台Action

负责逻辑处理的Action是UploadPicAction,它会利用JAVA的IO将图片保存在服务器网站一个名为upload的目录里:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
/**
*
*/
package com.ilovecl.test;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Date;
import java.util.ArrayList;
import java.util.List;

import org.apache.struts2.ServletActionContext;

import com.ilovecl.myproperty.model.RepairOrder;
import com.ilovecl.myproperty.service.IPostRepairOrderService;
import com.ilovecl.myproperty.util.MD5;
import com.opensymphony.xwork2.ActionSupport;

/**
* @author 邱永臣
*
*/
@SuppressWarnings("serial")
public class UploadPicAction extends ActionSupport {
private String problemDescription; // 故障物业的具体描述
private String place; // 故障物业的地点
private Date launchDate; // 保修单的上报时间

private String message;

private List<File> file; // 传到服务器里来的文件
private List<String> fileFileName; // 文件名
private List<String> fileContentType; // 文件类型
private List<String> dataUrl;

private IPostRepairOrderService postRepairOrderService;

/*
* (non-Javadoc)
*
* @see com.opensymphony.xwork2.ActionSupport#execute()
*/
@Override
public String execute() throws Exception {
this.message = "";

if (problemDescription.equals("")) {
this.message += "问题描述为空";
return INPUT;
} else if (place.equals("")) {
this.message += "物业故障地点为空";
return INPUT;
} else if (file == null || file.size() != 1) {
this.message += "现场图片为空为空";
return INPUT;
}

String md5 = saveImage();

return SUCCESS;
}

// 将学生上传的现场图片保存下来
private String saveImage() throws IOException {
String md5String = "";

dataUrl = new ArrayList<String>();

String imgpath = "upload/";

// 将文件存储在服务器中
for (int i = 0; i < file.size(); ++i) {
InputStream inputStream = new FileInputStream(file.get(i));

// 获取网站根目录的绝对路径(如C:\Users\DELL\Workspaces\MyEclipse Professional
// 2014\.metadata\.me_tcat\webapps\MyProperty\)
String rootPath = ServletActionContext.getServletContext()
.getRealPath("/");

dataUrl.add(imgpath + fileFileName.get(i));

File dest = new File(rootPath + imgpath, fileFileName.get(i));

// 得到经MD5编码后的文件名
md5String = MD5.getMD5(fileFileName.get(i));

// 利用JAVA的IO将图片存储在网站的upload目录里
OutputStream outputStream = new FileOutputStream(dest);

byte[] buffer = new byte[2048];

int len = 0;

while ((len = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
}

inputStream.close();
outputStream.close();
}

return md5String;
}

/**
* @return the problemDescription
*/
public String getProblemDescription() {
return problemDescription;
}

/**
* @param problemDescription
* the problemDescription to set
*/
public void setProblemDescription(String problemDescription) {
this.problemDescription = problemDescription;
}

/**
* @return the place
*/
public String getPlace() {
return place;
}

/**
* @param place
* the place to set
*/
public void setPlace(String place) {
this.place = place;
}

/**
* @return the launchDate
*/
public Date getLaunchDate() {
return launchDate;
}

/**
* @param launchDate
* the launchDate to set
*/
public void setLaunchDate(Date launchDate) {
this.launchDate = launchDate;
}

/**
* @return the message
*/
public String getMessage() {
return message;
}

/**
* @param message
* the message to set
*/
public void setMessage(String message) {
this.message = message;
}

/**
* @return the file
*/
public List<File> getFile() {
return file;
}

/**
* @param file
* the file to set
*/
public void setFile(List<File> file) {
this.file = file;
}

/**
* @return the fileFileName
*/
public List<String> getFileFileName() {
return fileFileName;
}

/**
* @param fileFileName
* the fileFileName to set
*/
public void setFileFileName(List<String> fileFileName) {
this.fileFileName = fileFileName;
}

/**
* @return the fileContentType
*/
public List<String> getFileContentType() {
return fileContentType;
}

/**
* @param fileContentType
* the fileContentType to set
*/
public void setFileContentType(List<String> fileContentType) {
this.fileContentType = fileContentType;
}

/**
* @return the dataUrl
*/
public List<String> getDataUrl() {
return dataUrl;
}

/**
* @param dataUrl
* the dataUrl to set
*/
public void setDataUrl(List<String> dataUrl) {
this.dataUrl = dataUrl;
}

/**
* @return the postRepairOrderService
*/
public IPostRepairOrderService getPostRepairOrderService() {
return postRepairOrderService;
}

/**
* @param postRepairOrderService
* the postRepairOrderService to set
*/
public void setPostRepairOrderService(
IPostRepairOrderService postRepairOrderService) {
this.postRepairOrderService = postRepairOrderService;
}

}

结果(R)

运行网站,首页显示如下:

选择图片并上传就可以成功地传到服务器上并保存起来了。