http://git.oschina.net/zhuqiang/smart-framework 跟着书上学习的 框架git地址
http://git.oschina.net/zhuqiang/mrweb 依赖上面框架的demo练习
今日学习的感悟,底层的东西真的好多。现在才终于明白,封装一个mvc框架是多么的不容易。要处理太多的事情了,而且还要提供易用的特性。就更加不容易了。向大神门致敬
优化Action参数
之前在我们在DispatcherServlet中实现,并支持action方法的使用如下:
DispatcherServlet 中 没有分情况的 统一把 param参数传递进去了
ReflectionUtil.invokeMethod(bean, actionMethod, param);
在 action中必须这样对应方法:
@Action(value = "/customer",method = "GET")
public View index(Param param){
List<Customer> customerList = customerService.getCustomerList();
return new View("customer.jsp").addModel("customerList",customerList);
}
但是在某些时候,我们并不像传递任何参数。那么这样的话,这个实现方式就得优化优化了。
其实自己想了下,还是按照书上的思路,来进行简单的优化吧。(因为如果要细想的话,这样的实现方式还有很多的问题。不过先入门吧。以后自己再去解决这些缺陷)
实现思路:
在DispatcherServlet中判断获取到的参数是否有值,有的话就把参数传递进去。
Method actionMethod = hander.getActionMethod();
Object result = null;
if(paramMap.isEmpty()){
result = ReflectionUtil.invokeMethod(bean,actionMethod);
}else{
// 把处理好的 请求参数 封装成 param 然后调用 hander
Param param = new Param(paramMap);
result = ReflectionUtil.invokeMethod(bean, actionMethod, param);
}
提供文件上传功能
讲解文件上传功能之前,先把之前遗留的问题给解决了。 在DispatcherServlet中 在见面几章的时候,有些疑问,代码的处理方式等,也没有全部的明白。今天在写完文件上传的时候,才终于把疑惑解开了,一共有以下几点:
- servletContext.getServletRegistration(“default”) :的作用,它的作用是,tomcat提供给我们的一个setvlet。用来处理静态资源的访问。 javaee只提供接口。实现就有tomcat来实现了。
- 最开始实现DispatcherServlet的时候说连图片都不能访问,今天解决了,原因是,要在第一步骤中做映射,还有一个条件是,这个映射的路径不能放到web-info目录下。
- 页面提交数据的格式:有以下3种,我们需要针对以下的数据格式进行解析。
- application/x-www-form-urlencoded:在发送前编码所有字符(默认)
- multipart/form-data:不对字符编码。在使用包含文件上传控件的表单时,必须使用该值。
- text/plain:空格转换为 “+” 加号,但不对特殊字符编码。
修改后的DispatcherServlet(粗糙版本)
/**
* Created by zhuqiang on 2015/10/21 0021.
* mvc 框架的核心
*/
@WebServlet(urlPatterns = "/*", loadOnStartup = 0)
public class DispatcherServlet extends HttpServlet {
@Override
public void init(ServletConfig config) throws ServletException {
HelperLoader.init(); // 就是加载之前封装好的 Helper等class
ServletContext servletContext = config.getServletContext();
// 查看了下 返回的是 org.apache.jasper.servlet.JspServlet 说明是框架中的一个servlet
ServletRegistration jspServlet = servletContext.getServletRegistration("jsp"); //获取注册的 js servlet对象
jspServlet.addMapping(ConfigHelper.getJspPath() + "*");
// 注册servlet。 使用默认的就是放行。 这里就是让客户端请求能获取到资源文件
// 符合该路径的,会被该servlet执行,而不会转调到我们自己的setvlet
ServletRegistration aDefault = servletContext.getServletRegistration("default"); //处理静态资源
aDefault.addMapping(ConfigHelper.getAssetPath() + "*", "/favicon.ico"); ///favicon.ico 是网站的标志图标也要放行
// 然后这个是所有的servlet . 里面有我们自己的 和 框架注册的
Map<String, ? extends ServletRegistration> servletRegistrations = servletContext.getServletRegistrations();
}
// 基础知识: 先进入service 在service方法中判断 然后调用不同的get啊post方法的
@Override
public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String reqMethod = req.getMethod().toLowerCase();
String reqPath = req.getPathInfo(); // 获取请求路径
Hander hander = ControllerHelper.getHander(reqMethod, reqPath); // 获取对应的处理类
Map<String, Object> paramMap = new HashMap<>();
if (hander != null) { //找到了对应的处理器
Class<?> controllerClass = hander.getControllerClass();
Object bean = BeanHelper.getBean(controllerClass); // 处理器实例
boolean isMultipart = ServletFileUpload.isMultipartContent(req);
if (isMultipart) {
multipartFormDataHander(req, paramMap);
} else {
/**
* 这样获取的方式是页面使用的 enctype="application/x-www-form-urlencoded" 以url encoded 提交的。
* 或则是在url后面添加的get参数什么的正常方式提交的
*/
req.setCharacterEncoding("UTF-8");
Enumeration<String> parameterNames = req.getParameterNames();
while (parameterNames.hasMoreElements()) {
String paramName = parameterNames.nextElement();
String paramValue = req.getParameter(paramName);
paramMap.put(paramName, paramValue);
}
String body = CodecUtil.decodeURL(StreameUtil.getString(req.getInputStream()));
if(StringUtils.isNotBlank(body)){
String[] params = StringUtils.split(body, "&");
for (String param : params) {
String[] array = StringUtils.split(param, "=");
if(ArrayUtils.isNotEmpty(array) && array.length ==2){
String paramName = array[0];
String paramValue = array[1];
paramMap.put(paramName,paramValue);
}
}
}
}
Method actionMethod = hander.getActionMethod();
Object result = null;
if (paramMap.isEmpty()) {
result = ReflectionUtil.invokeMethod(bean, actionMethod);
} else {
// 把处理好的 请求参数 封装成 param 然后调用 hander
Param param = new Param(paramMap);
result = ReflectionUtil.invokeMethod(bean, actionMethod, param);
}
// 处理 返回值, 需要区别对待 是 view 还是 data json数据
if (result instanceof View) {
View view = (View) result;
String path = view.getPath();
if (StringUtils.isNotBlank(path)) {
if (path.startsWith("/")) {
resp.sendRedirect(req.getContextPath() + path); //
} else { // 返回到页面的
Map<String, Object> model = view.getModel();
for (Map.Entry<String, Object> ent : model.entrySet()) {
req.setAttribute(ent.getKey(), ent.getValue());
}
req.getRequestDispatcher(ConfigHelper.getJspPath() + path).forward(req, resp);
}
}
} else if (result instanceof Data) {
Data data = (Data) result;
Object model = data.getModel();
if (model != null) {
resp.setContentType("application/json");
resp.setCharacterEncoding("UTF-8");
PrintWriter writer = resp.getWriter();
writer.write(new ObjectMapper().writeValueAsString(data));
writer.flush();
writer.close();
}
}
}
}
/**
* 处理 enctype="multipart/form-data" 的数据
* @param req
* @param paramMap
* @throws IOException
*/
private void multipartFormDataHander(HttpServletRequest req, Map<String, Object> paramMap) throws IOException {
/**
* 这里是 为页面提交数据: enctype="multipart/form-data" 的时候使用的
*/
// Create a factory for disk-based file items
DiskFileItemFactory factory = new DiskFileItemFactory();
// 当上传文件太大时,因为虚拟机能使用的内存是有限的,所以此时要通过临时文件来实现上传文件的保存
// 此方法是设置是否使用临时文件的临界值(单位:字节)
// factory.setSizeThreshold(yourMaxMemorySize);
// 与上一个结合使用,设置临时文件的路径(绝对路径)
factory.setRepository(new File("/temp"));
// Create a new file upload handler
ServletFileUpload upload = new ServletFileUpload(factory);
// 设置上传内容的大小限制(单位:字节)
// upload.setSizeMax(yourMaxRequestSize);
// Parse the request
List<?> items = null;
try {
items = upload.parseRequest(req);
} catch (FileUploadException e) {
e.printStackTrace();
}
Iterator iter = items.iterator();
while (iter.hasNext()) {
FileItem item = (FileItem) iter.next();
if (item.isFormField()) {
//如果是普通表单字段
String name = item.getFieldName();
String value = item.getString();
paramMap.put(name, value);
} else {
//如果是文件字段
String fieldName = item.getFieldName();
String fileName = item.getName();
String contentType = item.getContentType();
boolean isInMemory = item.isInMemory();
long sizeInBytes = item.getSize();
InputStream uploadedStream = item.getInputStream();
FileParam value = new FileParam(fileName, contentType, sizeInBytes, uploadedStream);// 自定义的bean对象,用来存储文件属性
paramMap.put(fieldName, value);
}
}
}
}
目标
也就是我们要开发的功能,让框架支持文件上传的功能。尽可能的模拟出。使用其他框架一样的文件上传功能。
页面的写法:
这里要说一点的是,我是主要做java后端的,所以前端的东西不太了解。看书上介绍了一个jquery.form.min.js。我感觉这个功能非常好。把平时一个表单提交直接转换成了ajax提交。还能在提交之前做一些校验什么的。非常的不错。有需要的话可以深入下。
<body>
<h1>新增客户</h1>
<form id="customer_from" enctype="text/plain">
<input type="hidden" name="id" value="${customer.id}">
<p>姓名:<input type="text" name="name" value="${customer.name}"/></p>
<p>联系地址:<input type="text" name="contact" value="${customer.contact}"/></p>
<p>电话号码:<input type="text" name="telephone" value="${customer.telephone}"/></p>
<p>邮箱:<input type="text" name="email" value="${customer.email}"/></p>
<p>照片:<input type="file" name="photo" value="${customer.photo}"/></p>
<p><button type="submit">保存</button></p>
</form>
<script src="${BASE}/asset/jquery-2.1.4.min.js"></script>
<script src="${BASE}/asset/jquery.form.min.js"></script>
<script>
$(function(){
// 使用 jquery.form 来把表单提交变成 ajax提交。提交可以得到返回的结果。
$("#customer_from").ajaxForm({
type:'post',
url:'${BASE}/customer_create',
success:function(data){
if(data){
alert("提交结果:" + data);
location.href = "${BASE}/customer";
}
}
});
});
</script>
</body>
实现文件上传功能
实现思路:
- 首先就是上面的DispatcherServlet.multipartFormDataHander 的处理。使用了apatch的commons-fileupload组件,来解析enctype=”multipart/form-data”的字段,然后再封装成我们自己的文件参数对象。
- 既然出现了isFormField(表单参数字段)和文件参数。那么就有必要优化下 Param对象了。让它能同时支持表单参数和文件参数。
优化Param对象
/**
* @author zhuqiang
* @version V1.0
* @date 2015/11/8 14:08
* 文件参数对象
*/
public class FileParam {
String fieldName; //参数名称
String fileName; //文件名称
String contentType; //文件类型
long fileSize; //文件大小
InputStream inputStream; //流
public FileParam(String fieldName, String fileName, String contentType, long fileSize, InputStream inputStream) {
this.fieldName = fieldName;
this.fileName = fileName;
this.contentType = contentType;
this.fileSize = fileSize;
this.inputStream = inputStream;
}
.......
}
**
* @author zhuqiang
* @version V1.0
* @date 2015/11/8 14:46
* 表单参数
*/
public class FormParam {
String fieldName; //参数名称
Object value; //值
public FormParam(String fieldName, Object value) {
this.fieldName = fieldName;
this.value = value;
}
....
}
/**
* Created by zhuqiang on 2015/10/21 0021.
* 封装请求参数
*/
public class Param{
private static final Logger LOGGER = LoggerFactory.getLogger(PropsUtil.class);
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;
}
/**
* 获取 表单参数映射
* @return
*/
public Map<String,Object> getFieldMap(){
Map<String,Object> fieldMap = new HashedMap<>();
if(CollectionUtils.isNotEmpty(formParamList)){
for (FormParam formParam : formParamList) {
String fieldName = formParam.getFieldName();
Object value = formParam.getValue();
if(fieldMap.containsKey(fieldName)){ //由于表单参数的处理有两种,parseParameterNames parseInputStream,正常的取和从流中取,在这里需要合并下相同的结果
value = fieldMap.get(fieldName) + StringUtil.SEPARATOR + value; //使用风隔符(自定义的逗号)分割开
}
fieldMap.put(fieldName,value);
}
}
return fieldMap;
}
/**
* 获取文件参数映射
* @return
*/
public Map<String,List<FileParam>> getFileMap(){
Map<String,List<FileParam>> fileParamMap = new HashedMap<>();
if(CollectionUtils.isNotEmpty(fileParamList)){
for (FileParam fileParam : fileParamList) {
String fieldName = fileParam.getFieldName();
List<FileParam> fileList;
if(fileParamMap.containsKey(fieldName)){ //处理多文件数组列表
fileList = fileParamMap.get(fieldName);
}else{
fileList = new ArrayList<>();
}
fileList.add(fileParam);
fileParamMap.put(fieldName,fileList);
}
}
return fileParamMap;
}
/**
* 按参数名称获取文件列表(多文件上传)
* @param filedName
* @return
*/
public List<FileParam> getFileList(String filedName){
return this.getFileMap().get(filedName);
}
/**
* 按参数名称获取 唯一的 文件对象(单文件上传)
* @param filedName
* @return
*/
public FileParam getFile(String filedName){
List<FileParam> fileList = this.getFileList(filedName);
if(CollectionUtils.isNotEmpty(fileList) && fileList.size() == 1){
return fileList.get(0);
}
LOGGER.info(String.format("filedName:%s is not find",filedName));
return null;
}
/** 验证参数是否为空 **/
public boolean isEmpty(){
return CollectionUtils.isEmpty(formParamList) && CollectionUtils.isEmpty(fileParamList);
}
/**
* 根据参数名 获取 string类型参数值
* @param name
* @return
*/
public String getString(String name){
return CastUtil.castString(getFieldMap().get(name));
}
public double getDouble(String name){
return CastUtil.castDouble(getFieldMap().get(name));
}
public long getLong(String name){
return CastUtil.castLong(getFieldMap().get(name));
}
public int getInt(String name){
return CastUtil.castInt(getFieldMap().get(name));
}
public boolean getBoolean(String name){
return CastUtil.castBoolean(getFieldMap().get(name));
}
}
重构DispatcherServlet转发器
思路上面都讲了。只是把上面那个粗糙版的重构下。现在我才真的佩服,有经验的开发人员就是牛逼。这样的重构方式我真的佩服。完全大变样了。
思路:
- 把处理请求参数的普通部分 封装成了一个对象
- 把处理请求参数的multipart/form-data部分封装成了一个对象
- 把处理返回结果的 封装了一个方法。
- 在 DispatcherServlet init中初始化处理multipart/form-data类型的一些设置
/**
* Created by zhuqiang on 2015/10/21 0021.
* mvc 框架的核心
*/
@WebServlet(urlPatterns = "/*", loadOnStartup = 0)
public class DispatcherServlet extends HttpServlet {
@Override
public void init(ServletConfig config) throws ServletException {
HelperLoader.init(); // 就是加载之前封装好的 Helper等class
ServletContext servletContext = config.getServletContext();
// 查看了下 返回的是 org.apache.jasper.servlet.JspServlet 说明是框架中的一个servlet
ServletRegistration jspServlet = servletContext.getServletRegistration("jsp"); //获取注册的 js servlet对象
jspServlet.addMapping(ConfigHelper.getJspPath() + "*");
// 注册servlet。 使用默认的就是放行。 这里就是让客户端请求能获取到资源文件
// 符合该路径的,会被该servlet执行,而不会转调到我们自己的setvlet
ServletRegistration aDefault = servletContext.getServletRegistration("default"); //处理静态资源
aDefault.addMapping(ConfigHelper.getAssetPath() + "*", "/favicon.ico"); ///favicon.ico 是网站的标志图标也要放行
// 然后这个是所有的servlet . 里面有我们自己的 和 框架注册的
Map<String, ? extends ServletRegistration> servletRegistrations = servletContext.getServletRegistrations();
UploadHelper.init(servletContext); //初始化文件请求数据类型
}
// 基础知识: 先进入service 在service方法中判断 然后调用不同的get啊post方法的
@Override
public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String reqMethod = req.getMethod().toLowerCase();
String reqPath = req.getPathInfo(); // 获取请求路径
Hander hander = ControllerHelper.getHander(reqMethod, reqPath); // 获取对应的处理类
Param param = null;
if (hander != null) { //找到了对应的处理器
Class<?> controllerClass = hander.getControllerClass();
Object bean = BeanHelper.getBean(controllerClass); // 处理器实例
boolean isMultipart = UploadHelper.isMultipart(req);
if (isMultipart) {
param = UploadHelper.createParam(req);
} else {
param = RequestHelper.createParam(req);
}
Method actionMethod = hander.getActionMethod();
Object result = null;
if (param.isEmpty()) {
result = ReflectionUtil.invokeMethod(bean, actionMethod);
} else {
result = ReflectionUtil.invokeMethod(bean, actionMethod, param);
}
handerModelAndView(req, resp, result);
}
}
/**
* 处理返回的结果。决定是跳转页面 还是 返回json数据
*
* @param req
* @param resp
* @param result
*
* @throws IOException
* @throws ServletException
*/
private void handerModelAndView(HttpServletRequest req, HttpServletResponse resp, Object result) throws IOException, ServletException {
// 处理 返回值, 需要区别对待 是 view 还是 data json数据
if (result instanceof View) {
View view = (View) result;
String path = view.getPath();
if (StringUtils.isNotBlank(path)) {
if (path.startsWith("/")) {
resp.sendRedirect(req.getContextPath() + path); //
} else { // 返回到页面的
Map<String, Object> model = view.getModel();
for (Map.Entry<String, Object> ent : model.entrySet()) {
req.setAttribute(ent.getKey(), ent.getValue());
}
req.getRequestDispatcher(ConfigHelper.getJspPath() + path).forward(req, resp);
}
}
} else if (result instanceof Data) {
Data data = (Data) result;
Object model = data.getModel();
if (model != null) {
resp.setContentType("application/json");
resp.setCharacterEncoding("UTF-8");
PrintWriter writer = resp.getWriter();
writer.write(new ObjectMapper().writeValueAsString(data));
writer.flush();
writer.close();
}
}
}
}
/**
* @author zhuqiang
* @version V1.0
* @date 2015/11/8 17:02
* 请求助手
*/
public final class RequestHelper {
/**
* 创建请求参数对象
* @param request
* @return
*/
public static Param createParam(HttpServletRequest request) throws IOException {
ArrayList<FormParam> formParamList = new ArrayList<>();
formParamList.addAll(parseParameterNames(request));
formParamList.addAll(parseInputStream(request));
return new Param(formParamList);
}
/**
* 解析参数
* @param req
* @return
*/
private static List<FormParam> parseParameterNames(HttpServletRequest req){
/**
* 这样获取的方式是页面使用的 enctype="application/x-www-form-urlencoded" 以url encoded 提交的。
* 或则是在url后面添加的get参数什么的正常方式提交的
*/
try {
req.setCharacterEncoding("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
List<FormParam> formParamList = new ArrayList<>();
Enumeration<String> parameterNames = req.getParameterNames();
while (parameterNames.hasMoreElements()) {
String paramName = parameterNames.nextElement();
String[] parameterValues = req.getParameterValues(paramName); // 如果页面传递的是相同name的参数。这里取到的就是一个数组
if(ArrayUtils.isNotEmpty(parameterValues)){
Object paramValue;
if(parameterValues.length == 1){
paramValue = parameterValues[0];
}else{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < parameterValues.length; i++) {
sb.append(parameterValues[i]);
if( i != parameterValues.length -1){
sb.append(StringUtil.SEPARATOR); //用逗号隔开
}
}
paramValue = sb.toString();
}
formParamList.add(new FormParam(paramName,paramValue));
}
}
return formParamList;
}
/**
* 从 流中解析参数。这里我还是没有明白 什么情况下会在流中出现这样类似的格式的字段,只知道xml的可以从流中解析出来
* @return
*/
private static List<FormParam> parseInputStream(HttpServletRequest req) throws IOException {
List<FormParam> formParamList = new ArrayList<>();
String body = CodecUtil.decodeURL(StreameUtil.getString(req.getInputStream()));
if(StringUtils.isNotBlank(body)){
String[] params = StringUtils.split(body, "&");
for (String param : params) {
String[] array = StringUtils.split(param, "=");
if(ArrayUtils.isNotEmpty(array) && array.length ==2){
String paramName = array[0];
String paramValue = array[1];
formParamList.add(new FormParam(paramName,paramValue));
}
}
}
return formParamList;
}
}
/**
* @author zhuqiang
* @version V1.0
* @date 2015/11/8 16:05
* 文件上传助手类
*/
public class UploadHelper {
private static final Logger LOGGER = LoggerFactory.getLogger(UploadHelper.class);
private static ServletFileUpload servletFileUpload; // apache 的文件上传对象
/**
* 初始化,文件上传对象
* @param servletContext
*/
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.getUploadLimit(); //获取 用户限制上传文件的大小
if(uploadLimit != 0){
servletFileUpload.setFileSizeMax(uploadLimit);
}
}
/**
* 判断该请求是否是 Multipart类型
* @param req
* @return
*/
public static boolean isMultipart(HttpServletRequest req){
return ServletFileUpload.isMultipartContent(req);
}
/**
* 创建请求参数
* @param req
* @return
*/
public static Param createParam(HttpServletRequest req) throws IOException {
List<FormParam> formParamList = new ArrayList<>(); //表单参数列表
List<FileParam> fileParamList = new ArrayList<>(); //文件参数列表
try {
Map<String, List<FileItem>> fileItemListMap = servletFileUpload.parseParameterMap(req);
if(MapUtils.isNotEmpty(fileItemListMap)) {
for(Map.Entry<String, List<FileItem>> ent : fileItemListMap.entrySet()){
String filedName = ent.getKey();
List<FileItem> value = ent.getValue();
if(CollectionUtils.isNotEmpty(value)){
for (FileItem fileItem : value) {
if(fileItem.isFormField()){ //如果是表单属性
String fieldValue = fileItem.getString("utf-8");
formParamList.add(new FormParam(filedName, fieldValue));
}else{ //如果是文件字段
String fileName = FileUtil.getRealFileName(new String(fileItem.getName().getBytes(),"UTF-8")); //获取真实文件名称,自动去掉路径,防止中文乱码
if(StringUtils.isNotBlank(fileName)){
String fieldName = fileItem.getFieldName(); //字段名称
long size = fileItem.getSize(); //文件大小
String contentType = fileItem.getContentType(); //文件类型
InputStream inputStream = fileItem.getInputStream(); //文件流
fileParamList.add(new FileParam(fieldName, fileName, contentType, size, inputStream));
}
}
}
}
}
}
} catch (FileUploadException e) {
LOGGER.error("create Param failure",e);
throw new RuntimeException(e);
}
return new Param(formParamList,fileParamList);
}
/**
* 上传文件
* @param basePath 基础绝对路径
* @param fileParam
*/
public static void uploadFile(String basePath,FileParam fileParam){
try {
if(fileParam != null){
String filePath = basePath + fileParam.getFileName();
FileUtil.createFile(filePath);
//使用 apache io工具进行图片的上传
BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(filePath));
IOUtils.copy(new BufferedInputStream(fileParam.getInputStream()), output);
output.flush();
output.close();
}
}catch (Exception e){
LOGGER.error("upload file failure",e);
throw new RuntimeException(e);
}
}
/**
* 批量上传文件
* @param basePath
* @param fileParamList
*/
public static void uploadFile(String basePath,List<FileParam> fileParamList){
try {
if(CollectionUtils.isNotEmpty(fileParamList)){
for (FileParam fileParam : fileParamList) {
if(fileParam != null){
String filePath = basePath + fileParam.getFileName();
FileUtil.createFile(filePath);
//使用 apache io工具进行图片的上传
IOUtils.copy(new BufferedInputStream(fileParam.getInputStream()),new BufferedOutputStream(new FileOutputStream(filePath)));
}
}
}
}catch (Exception e){
LOGGER.error("upload file failure",e);
throw new RuntimeException(e);
}
}
}
/**
* @author zhuqiang
* @version V1.0
* @date 2015/11/8 16:44
* 文件操作助手类
*/
public class FileUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(FileUtil.class);
/**
* 获取真实文件名称,自动去掉路径,防止中文乱码
*
* @param fileName 从页面file组件上传过来的路径加文件名称
*
* @return
*/
public static String getRealFileName(String fileName) {
return FilenameUtils.getName(fileName); // apache.io 的工具类
}
/**
* 创建文件夹
*
* @param filePath 文件真实路径
*
* @return
*/
public static File createFile(String filePath) {
File file = null;
try {
file = new File(filePath);
File parentDir = file.getParentFile(); //获取父目录
if (!parentDir.exists()) {
FileUtils.forceMkdir(parentDir);
}
} catch (IOException e) {
LOGGER.error("create file failure",e);
throw new RuntimeException(e);
}
return file;
}
}
/**
* @author zhuqiang
* @version V1.0
* @date 2015/11/8 15:31
*/
public class StringUtil {
/** 分隔符 */
public static final String SEPARATOR = String.valueOf((char)29);
}
在action中使用如下:
@Action(value = "/customer_create",method = "POST")
public Data create(Param param){
FileParam file = param.getFile("photo");
String basePath = "d:/asset/image/";
UploadHelper.uploadFile(basePath,file); //上传图片到指定的地址
Map<String, Object> fieldMap = param.getFieldMap();
fieldMap.remove("id"); //由于id是自增长的,移除掉
fieldMap.put("photo","/asset/image/" + file.getFileName());
boolean customer = customerService.createCustomer(fieldMap);
return new Data(true);
}
与ServletApi解耦
其实说与ServletApi解耦。只是我们把ServletApi给封装了下,目前的框架中,是不能使用request等api对象的。这个就需要我们使框架支持这个功能。最简单的思路,使用工具类。把一些常用的都封装进来
实现思路:
- 简单的使用工具类,但是使用ThreadLocal来封装每个请求所对应的ServletApi信息。
- 要注意这个工具类在注入ServletApi信息的时机,和消逝的时机。
- 注入的时机: 在DispatcherServlet中调用目标方法前
- 消逝的时机: 在DispatcherServlet中调用目标方法后
/**
* @author zhuqiang
* @version V1.0
* @date 2015/11/9 21:34
* servletApi助手,封装一些常用的 方法
*/
public class ServletHelper {
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(ServletHelper.class);
//为每个线程存储自己的servletHelper
private static final ThreadLocal<ServletHelper> SERVLET_HELPER_THREAD_LOCAL = new ThreadLocal<>();
private HttpServletRequest request;
private HttpServletResponse response;
private ServletHelper(HttpServletRequest request, HttpServletResponse response) {
this.request = request;
this.response = response;
}
/** 初始化**/
public static void init(HttpServletRequest request, HttpServletResponse response){
SERVLET_HELPER_THREAD_LOCAL.set(new ServletHelper(request,response));
}
/** 销毁 */
public static void destroy(){
SERVLET_HELPER_THREAD_LOCAL.remove();
}
/** 获取rquest对象**/
public static HttpServletRequest getRquest(){
return SERVLET_HELPER_THREAD_LOCAL.get().request;
}
/** 获取response对象**/
public static HttpServletResponse getResponse(){
return SERVLET_HELPER_THREAD_LOCAL.get().response;
}
/** 获取session对象**/
public static HttpSession getSession(){
return getRquest().getSession();
}
public static ServletContext getServletContext(){
return getRquest().getServletContext();
}
/** 将属性放入request中 */
public static void setRequestAttribute(String name,Object value){
getRquest().setAttribute(name,value);
}
/** 从request中获取值 */
public static Object getRequestAttribute(String name){
return getRquest().getAttribute(name);
}
/** 从request中移除 **/
public static void removeRequestAttribute(String name){
getRquest().removeAttribute(name);
}
public static void setSessionAttribute(String key,Object value){
getSession().setAttribute(key,value);
}
public static Object getSessionAttribute(String name){
return getSession().getAttribute(name);
}
public static void removeSessionAttribute(String name){
getSession().removeAttribute(name);
}
public static void invalidateSession(){
getSession().invalidate();
}
/** 重定向 **/
public static void sendRedirect(String location){
try {
getResponse().sendRedirect(location);
} catch (IOException e) {
LOGGER.error("sendRedirect fail",e);
throw new RuntimeException(e);
}
}
}
* 在DispatcherServlet中对该工具类的初始化和销毁*
@Override
public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletHelper.init(req,resp);
处理框架逻辑,转发hander等代码
...
ServletHelper.destroy();
}