动手写web框架(10): 文件上传

文件上传功能基于 Apache Commons FileUpload库。

重新定义Param,封装表单参数(FormParm)和文件参数(FileParam)。

Param

FormParam
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package org.smart4j.framework.bean;
/**
* 表单参数
* Created by Roger on 2017/2/17.
*/
public class FormParam {
private String fieldName;
private Object fieldValue;
public FormParam(String fieldName, Object fieldValue) {
this.fieldName = fieldName;
this.fieldValue = fieldValue;
}
public String getFieldName() {
return fieldName;
}
public Object getFieldValue() {
return fieldValue;
}
}
FileParam
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
package org.smart4j.framework.bean;
import java.io.InputStream;
/**
* 封装上传文件参数
* Created by Roger on 2017/2/17.
*/
public class FileParam {
// 文件表单的字段名
private String fieldName;
// 上传文件的文件名
private String fileName;
// 上传文件的文件大小
private long fileSize;
// 上传文件的Content-Type,可判断文件类型
private String contentType;
// 上传文件的字节输入流
private InputStream inputStream;
public FileParam(String fieldName, String fileName, long fileSize, String contentType, InputStream inputStream) {
this.fieldName = fieldName;
this.fileName = fileName;
this.fileSize = fileSize;
this.contentType = contentType;
this.inputStream = inputStream;
}
public String getFieldName() {
return fieldName;
}
public String getFileName() {
return fileName;
}
public long getFileSize() {
return fileSize;
}
public String getContentType() {
return contentType;
}
public InputStream getInputStream() {
return inputStream;
}
}
Param
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
package org.smart4j.framework.bean;
import org.apache.commons.collections4.CollectionUtils;
import org.smart4j.framework.util.CastUtil;
import org.smart4j.framework.util.StringUtil;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 请求参数对象
* 表单中所有的参数包括:表单参数FormParam和文件参数FileParam
* Created by Roger on 2016/11/23.
*/
public class Param {
private List<FormParam> formParamList;
private List<FileParam> fileParamList;
private Map<String, Object> fieldMap;
private Map<String, List<FileParam>> fileMap;
public Param(List<FormParam> formParamList) {
this.formParamList = formParamList;
createFieldMap(formParamList);
}
public Param(List<FormParam> formParamList, List<FileParam> fileParamList) {
this.formParamList = formParamList;
this.fileParamList = fileParamList;
createFieldMap(formParamList);
createFileMap(fileParamList);
}
private void createFieldMap(List<FormParam> formParamList){
fieldMap = new HashMap<>();
if (CollectionUtils.isNotEmpty(formParamList)){
for (FormParam formParam: formParamList){
String fieldName = formParam.getFieldName();
Object fieldValue = formParam.getFieldValue();
if (fileMap.containsKey(fieldName)){
fieldValue = fieldMap.get(fieldName) + StringUtil.SEPERATOR + fieldValue;
}
fieldMap.put(fieldName, fieldValue);
}
}
}
private void createFileMap(List<FileParam> fileParamList){
fileMap = new HashMap<>();
if (CollectionUtils.isNotEmpty(fileParamList)){
for (FileParam fileParam: fileParamList){
String fieldName = fileParam.getFieldName();
List<FileParam> fileParams;
if (fileMap.containsKey(fieldName)){
fileParams = fileMap.get(fieldName);
}else {
fileParams = new ArrayList<>();
}
fileParams.add(fileParam);
fileMap.put(fieldName, fileParams);
}
}
}
public long getLong(String name){
return CastUtil.castLong(fieldMap.get(name));
}
public int getInt(String name){
return CastUtil.castInt(fieldMap.get(name));
}
public double getDouble(String name){
return CastUtil.castDouble(fieldMap.get(name));
}
public boolean getBoolean(String name){
return CastUtil.castBoolean(fieldMap.get(name));
}
public Map<String, Object> getFieldMap() {
return fieldMap;
}
public Map<String, List<FileParam>> getFileMap() {
return fileMap;
}
/**
* 获取fieldName对应的所有上传文件
* @param fieldName
* @return
*/
public List<FileParam> getFileList(String fieldName){
return fileMap.get(fieldName);
}
/**
* 获取fieldName对应的唯一上传文件
* @param fieldName
* @return
*/
public FileParam getFile(String fieldName){
List<FileParam> fileParamList = fileMap.get(fieldName);
if (CollectionUtils.isNotEmpty(fileParamList) && fileParamList.size() == 1){
return fileParamList.get(0);
}
return null;
}
/**
* 验证参数是否为空
* @return
*/
public boolean isEmpty(){
return CollectionUtils.isEmpty(formParamList) && CollectionUtils.isEmpty(fileParamList);
}
}

UploadHelper

通过UploadHelper类来封装 Apache Commons FileUpload的相关代码。

UploadHelper
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
package org.smart4j.framework.helper;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.smart4j.framework.bean.FileParam;
import org.smart4j.framework.bean.FormParam;
import org.smart4j.framework.bean.Param;
import org.smart4j.framework.util.FileUtil;
import org.smart4j.framework.util.StreamUtil;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 文件上传助手类
* Created by Roger on 2017/2/17.
*/
public class UploadHelper {
private static final Logger LOGGER = LoggerFactory.getLogger(UploadHelper.class);
/**
* Apache Commons FileUpload 提供的 Servlet 文件上传对象
*/
private static ServletFileUpload servletFileUpload;
/**
* 初始化
* 需要社会ServletFileUpload对象的上传文件临时目录和上传文件的最大限制
*/
public static void init(ServletContext servletContext){
// 文件上传的临时目录 (应用服务器的临时目录)
File repository = (File)servletContext.getAttribute("javax.servlet.context.tmpdir");
servletFileUpload = new ServletFileUpload(new DiskFileItemFactory(DiskFileItemFactory.DEFAULT_SIZE_THRESHOLD, repository));
int uploadLimit = ConfigHelper.getAppFileUploadLimit();
if (uploadLimit != 0){
servletFileUpload.setFileSizeMax(uploadLimit * 1024 * 1024);
}
}
/**
* 判断请求是否为 multipart 类型
* @param request
* @return
*/
public static boolean isMultipart(HttpServletRequest request){
return ServletFileUpload.isMultipartContent(request);
}
/**
* 封装请求参数
* @param request
* @return
*/
public static Param createParam(HttpServletRequest request) throws IOException {
List<FormParam> formParamList = new ArrayList<>();
List<FileParam> fileParamList = new ArrayList<>();
try {
Map<String, List<FileItem>> fileItemMap = servletFileUpload.parseParameterMap(request);
if (MapUtils.isNotEmpty(fileItemMap)){
for (Map.Entry<String, List<FileItem>> entry: fileItemMap.entrySet()){
String fieldName = entry.getKey();
List<FileItem> fileItemList = entry.getValue();
if (CollectionUtils.isNotEmpty(fileItemList)){
for (FileItem fileItem: fileItemList){
if (fileItem.isFormField()){
String fieldValue = fileItem.getString("UTF-8");
formParamList.add(new FormParam(fieldName, fieldValue));
}else {
String fileName = FileUtil.getRealFileName(new String(fileItem.getName().getBytes(), "UTF-8"));
if (StringUtils.isNotEmpty(fileName)){
long fileSize = fileItem.getSize();
String contentType = fileItem.getContentType();
InputStream inputStream = fileItem.getInputStream();
fileParamList.add(new FileParam(fieldName, fileName, fileSize, contentType, inputStream));
}
}
}
}
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
return new Param(formParamList, fileParamList);
}
/**
* 上传文件
* @param basePath
* @param fileParam
*/
public static void uploadFile(String basePath, FileParam fileParam){
if (fileParam != null){
String filePath = basePath + fileParam.getFileName();
FileUtil.createFile(filePath);
InputStream inputStream = new BufferedInputStream(fileParam.getInputStream());
try {
OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(filePath));
StreamUtil.copyStream(inputStream, outputStream);
} catch (FileNotFoundException e) {
LOGGER.error("upload file failure", e);
throw new RuntimeException(e);
}
}
}
/**
* 批量上传
* @param basePath
* @param fileParamList
*/
public static void uploadFile(String basePath, List<FileParam> fileParamList){
if (CollectionUtils.isNotEmpty(fileParamList)){
for (FileParam fileParam: fileParamList){
uploadFile(basePath, fileParam);
}
}
}
}

RequestHelper

通过RequestHelper类封装之前实现的普通请求参数处理。

RequestHelper
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
package org.smart4j.framework.helper;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.smart4j.framework.bean.FormParam;
import org.smart4j.framework.bean.Param;
import org.smart4j.framework.util.CodecUtil;
import org.smart4j.framework.util.StreamUtil;
import org.smart4j.framework.util.StringUtil;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
/**
* 请求助手类
* Created by Roger on 2017/2/17.
*/
public class RequestHelper {
public static Param createParam(HttpServletRequest request) throws IOException {
List<FormParam> formParamList = new ArrayList<>();
formParamList.addAll(parseParameterNames(request));
formParamList.addAll(parseInputStream(request));
return new Param(formParamList);
}
private static List<FormParam> parseParameterNames(HttpServletRequest request) {
List<FormParam> formParamList = new ArrayList<>();
Enumeration<String> paramNames = request.getParameterNames();
while (paramNames.hasMoreElements()) {
String fieldName = paramNames.nextElement();
String[] fieldValues = request.getParameterValues(fieldName);
if (ArrayUtils.isNotEmpty(fieldValues)) {
Object fieldValue;
if (fieldValues.length == 1) {
fieldValue = fieldValues[0];
} else {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < fieldValues.length; i++) {
sb.append(fieldValues[i]);
if (i != fieldValues.length - 1) {
sb.append(StringUtil.SEPERATOR);
}
}
fieldValue = sb.toString();
}
formParamList.add(new FormParam(fieldName, fieldValue));
}
}
return formParamList;
}
private static List<FormParam> parseInputStream(HttpServletRequest request) throws IOException {
List<FormParam> formParamList = new ArrayList<>();
String body = CodecUtil.decodeURL(StreamUtil.getString(request.getInputStream()));
if (StringUtils.isNotEmpty(body)) {
String[] kvs = StringUtils.split(body, "&");
if (ArrayUtils.isNotEmpty(kvs)) {
for (String kv : kvs) {
String[] array = StringUtils.split(kv, "=");
if (ArrayUtils.isNotEmpty(array)) {
String fieldName = array[0];
String fieldValue = array[1];
formParamList.add(new FormParam(fieldName, fieldValue));
}
}
}
}
return formParamList;
}
}

DispatcherServlet

增加了文件上传功能后,相应的DispatcherServlet部分也要进行重构。

DispatcherServlet
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
package org.smart4j.framework;
import org.apache.commons.lang3.StringUtils;
import org.smart4j.framework.bean.Data;
import org.smart4j.framework.bean.Handler;
import org.smart4j.framework.bean.Param;
import org.smart4j.framework.bean.View;
import org.smart4j.framework.helper.*;
import org.smart4j.framework.util.JsonUtil;
import org.smart4j.framework.util.ReflectionUtil;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.Map;
/**
* Created by Roger on 2016/11/23.
*/
@WebServlet(urlPatterns = "/*", loadOnStartup = 0)
public class DispatcherServlet extends HttpServlet {
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
// 初始化Helper类
HelperLoader.init();
ServletContext servletContext = config.getServletContext();
// 注册处理jsp的servlet
// 传入一个"jsp"参数, 意味着从容器中获取JspServlet(这个Servlet由容器实现, The JSP page compiler and execution servlet)
ServletRegistration jspServlet = servletContext.getServletRegistration("jsp");
jspServlet.addMapping(ConfigHelper.getAppJspPath() + "*");
// 注册处理静态资源的默认servlet
// 传入一个"default"参数, 意味着从容器中获取DefaultServlet(这个Servlet由容器实现, 它负责处理普通的静态资源响应)
ServletRegistration defaultServlet = servletContext.getServletRegistration("default");
defaultServlet.addMapping(ConfigHelper.getAppAssetPath() + "*");
// 初始化文件上传助手类
UploadHelper.init(servletContext);
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse rsp) throws ServletException, IOException {
// 获取请求方法与请求路径
String requestMethod = req.getMethod().toLowerCase();
String requestPath = req.getPathInfo();
// 过滤对 favicon.ico 的访问
if (requestPath.equals("/favicon.ico")) {
return;
}
// 获取Action处理器
Handler handler = ControllerHelper.getHandler(requestMethod, requestPath);
if (handler != null) {
// 获取Controller类及其实例
Class<?> controllerCls = handler.getControllerClass();
Object controllerBean = BeanHelper.getBean(controllerCls);
Param param;
if (UploadHelper.isMultipart(req)){
param = UploadHelper.createParam(req);
}else {
param = RequestHelper.createParam(req);
}
// 调用Action方法
Method actionMethod = handler.getActionMethod();
Object result = ReflectionUtil.invokeMethod(controllerBean, actionMethod, param);
// 处理Action方法返回值
if (result instanceof View) {
handleViewResult((View) result, req, rsp);
} else if (result instanceof Data) {
// 返回json数据
handleDataResult((Data) result, rsp);
}
}
}
private void handleViewResult(View view, HttpServletRequest req, HttpServletResponse rsp) throws IOException, ServletException {
String path = view.getPath();
if (StringUtils.isNotEmpty(path)) {
if (path.startsWith("/")) {
rsp.sendRedirect(req.getContextPath() + path);
} else {
Map<String, Object> model = view.getModel();
for (Map.Entry<String, Object> entry : model.entrySet()) {
req.setAttribute(entry.getKey(), entry.getValue());
}
req.getRequestDispatcher(ConfigHelper.getAppJspPath() + path).forward(req, rsp);
}
}
}
private void handleDataResult(Data data, HttpServletResponse rsp) throws IOException {
Object model = data.getModel();
if (model != null) {
rsp.setContentType("application/json");
rsp.setCharacterEncoding("UTF-8");
PrintWriter writer = rsp.getWriter();
writer.write(JsonUtil.toJson(model));
writer.flush();
writer.close();
}
}
}

使用

在 Action 处理方法中使用文件上传功能:

1
2
3
4
5
6
@Action("post/uploadfile")
public Data upload(Param param){
FileParam fileParam = param.getFile("fieldName");
UploaderHelper.upload("/tmp/upload/", fileParam);
return new DAta("upload success");
}