[笔记-架构探险]框架优化与功能扩展3.1.优化Action参数、提供文件上传功能、与ServletApi解耦.

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中 在见面几章的时候,有些疑问,代码的处理方式等,也没有全部的明白。今天在写完文件上传的时候,才终于把疑惑解开了,一共有以下几点:

  1. servletContext.getServletRegistration(“default”) :的作用,它的作用是,tomcat提供给我们的一个setvlet。用来处理静态资源的访问。 javaee只提供接口。实现就有tomcat来实现了。
  2. 最开始实现DispatcherServlet的时候说连图片都不能访问,今天解决了,原因是,要在第一步骤中做映射,还有一个条件是,这个映射的路径不能放到web-info目录下。
  3. 页面提交数据的格式:有以下3种,我们需要针对以下的数据格式进行解析。
    1. application/x-www-form-urlencoded:在发送前编码所有字符(默认)
    2. multipart/form-data:不对字符编码。在使用包含文件上传控件的表单时,必须使用该值。
    3. 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>

实现文件上传功能

实现思路:

  1. 首先就是上面的DispatcherServlet.multipartFormDataHander 的处理。使用了apatch的commons-fileupload组件,来解析enctype=”multipart/form-data”的字段,然后再封装成我们自己的文件参数对象。
  2. 既然出现了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转发器

思路上面都讲了。只是把上面那个粗糙版的重构下。现在我才真的佩服,有经验的开发人员就是牛逼。这样的重构方式我真的佩服。完全大变样了。
思路:

  1. 把处理请求参数的普通部分 封装成了一个对象
  2. 把处理请求参数的multipart/form-data部分封装成了一个对象
  3. 把处理返回结果的 封装了一个方法。
  4. 在 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对象的。这个就需要我们使框架支持这个功能。最简单的思路,使用工具类。把一些常用的都封装进来
  
实现思路:

  1. 简单的使用工具类,但是使用ThreadLocal来封装每个请求所对应的ServletApi信息。
  2. 要注意这个工具类在注入ServletApi信息的时机,和消逝的时机。
  3. 注入的时机: 在DispatcherServlet中调用目标方法前
  4. 消逝的时机: 在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();
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值