需求:
一个form的enctype类型为multipart/form-data,采用jquery和jquery form使用ajax来上传表单。
在这里,在smart项目中创建web文件结构:src/main/webapp/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app 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_3_0.xsd" version="3.0"> </web-app>
在webapp下创建asset文件夹,WEB-INF下创建view文件夹,view下创建uploadSample.jsp文件。
<%@ page pageEncoding="UTF-8" %> <html> <head> <title>客户管理 - 创建客户</title> </head> <body> <h1><a href="${BASE}/">首页</a> / <a href="${BASE}/customer">客户管理</a> / 创建客户</h1> <form id="customer_form" enctype="multipart/form-data"> <table> <tr> <td>客户名称:</td> <td> <input type="text" name="name" value="${customer.name}"> </td> </tr> <tr> <td>联系人:</td> <td> <input type="text" name="contact" value="${customer.contact}"> </td> </tr> <tr> <td>电话号码:</td> <td> <input type="text" name="telephone" value="${customer.telephone}"> </td> </tr> <tr> <td>邮箱地址:</td> <td> <input type="text" name="email" value="${customer.email}"> </td> </tr> <tr> <td>照片:</td> <td> <input type="file" name="photo" value="${customer.photo}"> </td> </tr> </table> <button type="submit">保存</button> </form> <script src="${BASE}/asset/lib/jquery/jquery.min.js"></script> <script src="${BASE}/asset/lib/jquery-form/jquery.form.min.js"></script> <script> $(function() { $('#customer_form').ajaxForm({ type: 'post', url: '${BASE}/customer_create', success: function(data) { if (data) { location.href = '${BASE}/customer'; } } }); }); </script> </body> </html>
asset lib下放入jquery和jqueryform的js文件(略)。
当表单提交时,请求发至CustomerController的createSubmit方法上。Controller的思路为:获取键值对,并从param参数中获取file。然后传入service,完成操作。DatabaseHelper用于数据库操作,需要建立一个UploadHelper用于上传文件。
/** * 处理 创建客户 请求 */ @Action("post:/customer_create") public Data createSubmit(Param param) { Map<String, Object> fieldMap = param.getFieldMap(); FileParam fileParam = param.getFile("photo"); boolean result = customerService.createCustomer(fieldMap, fileParam); return new Data(result); }
Service
/** * 创建客户 */ @Transaction public boolean createCustomer(Map<String, Object> fieldMap, FileParam fileParam) { boolean result = DatabaseHelper.insertEntity(Customer.class, fieldMap); if (result) { UploadHelper.uploadFile("/tmp/upload/", fileParam); } return result; }
实现上传功能:
思路是:重构Param类,封装FileParam和FormParam两个Bean。如此Param可以用于接收请求带来的各类参数了。
构建Param对象要注意参数名相同的情况,对于FormParam,值采用逗号相接。对于FileParam采用List表进行存储。
上述的步骤可以用于接收Param参数。接着建立一个UploadHelper,UploadHelper封装了Apache Commons FileUpload相关代码。
UploadHelper的作用有:
在DispatcherServlet中调用UploadHelper的init方法进行初始化。同时要进行Service的重构,跳过/favicon.ico请求,只处理普通请求,然后判断是否为multipart来创建不同的Param对象。对代码进行封装一下,提供一个RequestHelper类,通过createParam方法初始化Param对象。
下边是实现过程:
FileParam、FormParam和Param的重构。
package org.smart.framework.bean; import java.io.InputStream; /** * 封装上传文件参数 * * @author huangyong * @since 1.0.0 */ public class FileParam { private String fieldName; private String fileName; private long fileSize; 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; } }
package org.smart.framework.bean; /** * 封装表单参数 * * @author huangyong * @since 1.0.0 */ 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; } }
package org.smart.framework.bean; import org.smart.framework.util.CastUtil; import org.smart.framework.util.CollectionUtil; import org.smart.framework.util.StringUtil; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 请求参数对象 * * @author huangyong * @since 1.0.0 */ public class Param { private List<FormParam> formParamList; private List<FileParam> fileParamList; public Param(List<FormParam> formParamList) { this.formParamList = formParamList; } public Param(List<FormParam> formParamList, List<FileParam> fileParamList) { this.formParamList = formParamList; this.fileParamList = fileParamList; } /** * 获取请求参数映射 */ public Map<String, Object> getFieldMap() { Map<String, Object> fieldMap = new HashMap<String, Object>(); if (CollectionUtil.isNotEmpty(formParamList)) { for (FormParam formParam : formParamList) { String fieldName = formParam.getFieldName(); Object fieldValue = formParam.getFieldValue(); if (fieldMap.containsKey(fieldName)) { fieldValue = fieldMap.get(fieldName) + StringUtil.SEPARATOR + fieldValue; } fieldMap.put(fieldName, fieldValue); } } return fieldMap; } /** * 获取上传文件映射 */ public Map<String, List<FileParam>> getFileMap() { Map<String, List<FileParam>> fileMap = new HashMap<String, List<FileParam>>(); if (CollectionUtil.isNotEmpty(fileParamList)) { for (FileParam fileParam : fileParamList) { String fieldName = fileParam.getFieldName(); List<FileParam> fileParamList; if (fileMap.containsKey(fieldName)) { fileParamList = fileMap.get(fieldName); } else { fileParamList = new ArrayList<FileParam>(); } fileParamList.add(fileParam); fileMap.put(fieldName, fileParamList); } } return fileMap; } /** * 获取所有上传文件 */ public List<FileParam> getFileList(String fieldName) { return getFileMap().get(fieldName); } /** * 获取唯一上传文件 */ public FileParam getFile(String fieldName) { List<FileParam> fileParamList = getFileList(fieldName); if (CollectionUtil.isNotEmpty(fileParamList) && fileParamList.size() == 1) { return fileParamList.get(0); } return null; } /** * 验证参数是否为空 */ public boolean isEmpty() { return CollectionUtil.isEmpty(formParamList) && CollectionUtil.isEmpty(fileParamList); } /** * 根据参数名获取 String 型参数值 */ public String getString(String name) { return CastUtil.castString(getFieldMap().get(name)); } /** * 根据参数名获取 double 型参数值 */ public double getDouble(String name) { return CastUtil.castDouble(getFieldMap().get(name)); } /** * 根据参数名获取 long 型参数值 */ public long getLong(String name) { return CastUtil.castLong(getFieldMap().get(name)); } /** * 根据参数名获取 int 型参数值 */ public int getInt(String name) { return CastUtil.castInt(getFieldMap().get(name)); } /** * 根据参数名获取 boolean 型参数值 */ public boolean getBoolean(String name) { return CastUtil.castBoolean(getFieldMap().get(name)); } }
接下来写UploaderHelper,它需要添加依赖、配置上传限制、FileUtil。
<!-- Apache Commons FileUpload --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency>
ConfigConstant加入
String APP_UPLOAD_LIMIT = "smart.framework.app.upload_limit";
ConfigHelper中加入getAppUploadLimit()方法,若不在smart.properties中进行配置,则默认为10.
/** * 获取应用文件上传限制 */ public static int getAppUploadLimit() { return PropsUtil.getInt(CONFIG_PROPS, ConfigConstant.APP_UPLOAD_LIMIT, 10); }
FileUtil
package org.smart.framework.util; import java.io.File; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 文件操作工具类 * * @author huangyong * @since 1.0.0 */ public final class FileUtil { private static final Logger LOGGER = LoggerFactory.getLogger(FileUtil.class); /** * 获取真实文件名(自动去掉文件路径) */ public static String getRealFileName(String fileName) { return FilenameUtils.getName(fileName); } /** * 创建文件 */ public static File createFile(String filePath) { File file; try { file = new File(filePath); File parentDir = file.getParentFile(); if (!parentDir.exists()) { FileUtils.forceMkdir(parentDir); } } catch (Exception e) { LOGGER.error("create file failure", e); throw new RuntimeException(e); } return file; } }
最后就是UploadHelper
package org.smart.framework.helper; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; 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.slf4j.Logger; import org.slf4j.LoggerFactory; import org.smart.framework.bean.FileParam; import org.smart.framework.bean.FormParam; import org.smart.framework.bean.Param; import org.smart.framework.util.CollectionUtil; import org.smart.framework.util.FileUtil; import org.smart.framework.util.StreamUtil; import org.smart.framework.util.StringUtil; /** * 文件上传助手类 * * @author huangyong * @since 1.0.0 */ public final class UploadHelper { private static final Logger LOGGER = LoggerFactory.getLogger(UploadHelper.class); private static ServletFileUpload servletFileUpload; /** * 初始化 */ public static void init(ServletContext servletContext) { File repository = (File) servletContext.getAttribute("javax.servlet.context.tempdir"); servletFileUpload = new ServletFileUpload(new DiskFileItemFactory(DiskFileItemFactory.DEFAULT_SIZE_THRESHOLD, repository)); int uploadLimit = ConfigHelper.getAppUploadLimit(); if (uploadLimit != 0) { servletFileUpload.setFileSizeMax(uploadLimit * 1024 * 1024); } } /** * 判断请求是否为 multipart 类型 */ public static boolean isMultipart(HttpServletRequest request) { return ServletFileUpload.isMultipartContent(request); } /** * 创建请求对象 */ public static Param createParam(HttpServletRequest request) throws IOException { List<FormParam> formParamList = new ArrayList<FormParam>(); List<FileParam> fileParamList = new ArrayList<FileParam>(); try { Map<String, List<FileItem>> fileItemListMap = servletFileUpload.parseParameterMap(request); if (CollectionUtil.isNotEmpty(fileItemListMap)) { for (Map.Entry<String, List<FileItem>> fileItemListEntry : fileItemListMap.entrySet()) { String fieldName = fileItemListEntry.getKey(); List<FileItem> fileItemList = fileItemListEntry.getValue(); if (CollectionUtil.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 (StringUtil.isNotEmpty(fileName)) { long fileSize = fileItem.getSize(); String contentType = fileItem.getContentType(); InputStream inputSteam = fileItem.getInputStream(); fileParamList.add(new FileParam(fieldName, fileName, fileSize, contentType, inputSteam)); } } } } } } } catch (FileUploadException e) { LOGGER.error("create param failure", e); throw new RuntimeException(e); } return new Param(formParamList, fileParamList); } /** * 上传文件 */ public static void uploadFile(String basePath, FileParam fileParam) { try { if (fileParam != null) { String filePath = basePath + fileParam.getFileName(); FileUtil.createFile(filePath); InputStream inputStream = new BufferedInputStream(fileParam.getInputStream()); OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(filePath)); StreamUtil.copyStream(inputStream, outputStream); } } catch (Exception e) { LOGGER.error("upload file failure", e); throw new RuntimeException(e); } } /** * 批量上传文件 */ public static void uploadFile(String basePath, List<FileParam> fileParamList) { try { if (CollectionUtil.isNotEmpty(fileParamList)) { for (FileParam fileParam : fileParamList) { uploadFile(basePath, fileParam); } } } catch (Exception e) { LOGGER.error("upload file failure", e); throw new RuntimeException(e); } } }
Param变了,那么接下来就重构DispatcherHelper。首先是RequestHelper。
package org.smart.framework.helper; import org.smart.framework.bean.FormParam; import org.smart.framework.bean.Param; import org.smart.framework.util.ArrayUtil; import org.smart.framework.util.CodecUtil; import org.smart.framework.util.StreamUtil; import org.smart.framework.util.StringUtil; import java.io.IOException; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import javax.servlet.http.HttpServletRequest; /** * 请求助手类 * * @author huangyong * @since 1.0.0 */ public final class RequestHelper { /** * 创建请求对象 */ public static Param createParam(HttpServletRequest request) throws IOException { List<FormParam> formParamList = new ArrayList<FormParam>(); formParamList.addAll(parseParameterNames(request)); formParamList.addAll(parseInputStream(request)); return new Param(formParamList); } private static List<FormParam> parseParameterNames(HttpServletRequest request) { List<FormParam> formParamList = new ArrayList<FormParam>(); Enumeration<String> paramNames = request.getParameterNames(); while (paramNames.hasMoreElements()) { String fieldName = paramNames.nextElement(); String[] fieldValues = request.getParameterValues(fieldName); if (ArrayUtil.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.SEPARATOR); } } 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<FormParam>(); String body = CodecUtil.decodeURL(StreamUtil.getString(request.getInputStream())); if (StringUtil.isNotEmpty(body)) { String[] kvs = StringUtil.splitString(body, "&"); if (ArrayUtil.isNotEmpty(kvs)) { for (String kv : kvs) { String[] array = StringUtil.splitString(kv, "="); if (ArrayUtil.isNotEmpty(array) && array.length == 2) { String fieldName = array[0]; String fieldValue = array[1]; formParamList.add(new FormParam(fieldName, fieldValue)); } } } } return formParamList; } }
package org.smart.framework; import org.smart.framework.bean.Data; import org.smart.framework.bean.Handler; import org.smart.framework.bean.Param; import org.smart.framework.bean.View; import org.smart.framework.helper.*; import org.smart.framework.util.JsonUtil; import org.smart.framework.util.ReflectionUtil; import org.smart.framework.util.StringUtil; import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.Method; import java.util.Map; 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; /** * 请求转发器 * * @author huangyong * @since 1.0.0 */ @WebServlet(urlPatterns = "/*", loadOnStartup = 0) public class DispatcherServlet extends HttpServlet { @Override public void init(ServletConfig servletConfig) throws ServletException { HelperLoader.init(); ServletContext servletContext = servletConfig.getServletContext(); registerServlet(servletContext); UploadHelper.init(servletContext); } private void registerServlet(ServletContext servletContext) { ServletRegistration jspServlet = servletContext.getServletRegistration("jsp"); jspServlet.addMapping("/index.jsp"); jspServlet.addMapping(ConfigHelper.getAppJspPath() + "*"); ServletRegistration defaultServlet = servletContext.getServletRegistration("default"); defaultServlet.addMapping("/favicon.ico"); defaultServlet.addMapping(ConfigHelper.getAppAssetPath() + "*"); } @Override public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //ServletHelper.init(request, response); try { String requestMethod = request.getMethod().toLowerCase(); String requestPath = request.getPathInfo(); Handler handler = ControllerHelper.getHandler(requestMethod, requestPath); if (handler != null) { Class<?> controllerClass = handler.getControllerClass(); Object controllerBean = BeanHelper.getBean(controllerClass); Param param; if (UploadHelper.isMultipart(request)) { param = UploadHelper.createParam(request); } else { param = RequestHelper.createParam(request); } Object result; Method actionMethod = handler.getActionMethod(); if (param.isEmpty()) { result = ReflectionUtil.invokeMethod(controllerBean, actionMethod); } else { result = ReflectionUtil.invokeMethod(controllerBean, actionMethod, param); } if (result instanceof View) { handleViewResult((View) result, request, response); } else if (result instanceof Data) { handleDataResult((Data) result, response); } } } finally { ServletHelper.destroy(); } } private void handleViewResult(View view, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { String path = view.getPath(); if (StringUtil.isNotEmpty(path)) { if (path.startsWith("/")) { response.sendRedirect(request.getContextPath() + path); } else { Map<String, Object> model = view.getModel(); for (Map.Entry<String, Object> entry : model.entrySet()) { request.setAttribute(entry.getKey(), entry.getValue()); } request.getRequestDispatcher(ConfigHelper.getAppJspPath() + path).forward(request, response); } } } private void handleDataResult(Data data, HttpServletResponse response) throws IOException { Object model = data.getModel(); if (model != null) { response.setContentType("application/json"); response.setCharacterEncoding("UTF-8"); PrintWriter writer = response.getWriter(); String json = JsonUtil.toJson(model); writer.write(json); writer.flush(); writer.close(); } } }
封装完毕。