BaseServlet,让一个servlet处理多个请求

BaseServlet

  第一次学习servlet的时候是跟着传智播客免费的教学视频,其中崔希凡讲的是我学过自认讲的最好的一位,BaseServlet也是跟着他写过一次,当时很多东西不能理解,后来慢慢发现其中的内层深意,本工具类在崔老师的基础之上增加了文件下载功能,如果能很好掌握,非常有利对struts2的掌握!


1. 我们希望在一个Servlet中可以有多个请求处理方法!
2. 客户端发出请求时,必须给出一个参数,来说明要调用哪一个方法
3. 客户端必须传递名为method的参数

http://localhost:8080/test/UserServlet?method=login、http://localhost:8080/test/UserServlet?method=regist等等

原理

  BaseServlet中调用request.getParameter("method")来确定你需要调用的方法,因为每次请求都是执行service方法,所以通过反射来执行

BaseServlet

package hui.zhang.servlet;

import hui.zhang.down.DownUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;

/**
 * BaseServlet
 * 我们希望在一个Servlet中处理多个请求
 * 创建Servlet时继承本类而不是继承HttpServlet,重写service方法
 * 客户端在发起请求时需要传递method参数来判断调用哪个方法
 * eg:http://localhost:8080/test/UserServlet?method=login
 * 返回值"f:/xxx"为转发、"r:/xxx"为重定向、"d:/xxx"为下载
 * 重定向可以重定向到其他项目中,写法:"r:/192.168.11.24:8080/example/index.jsp"
 * 下载可以下载服务器中目录下的文件 "d:/WEB-INF/a.jpg"
 * 也可以下载磁盘绝对路径下的文件 "d:/G:/a.jpg"
 * @author hui.zhang
 *
 */
@SuppressWarnings("serial")
public class BaseServlet extends HttpServlet {
    @Override
    public void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");//处理响应编码
        
        //获取传递的method参数
        String methodName = request.getParameter("method");
        if (methodName == null || methodName.trim().isEmpty()) {
            throw new RuntimeException("没有传递method参数,不能确定要调用的方法!");
        }
        //得到当前类的class对象
        Class c  = getClass();
        Method method = null;
        try {
            method = c.getMethod(methodName, 
                    HttpServletRequest.class,HttpServletResponse.class);
        } catch (Exception e) {
            throw new RuntimeException("要调用的方法:"+methodName+"(HttpServletRequest,HttpServletResponse),不存在!",e);
        } 
        //调用method表示的方法
        try {
            String result = (String)method.invoke(this, request,response);
            /*
             * 获取请求处理方法执行后返回的字符串,它表示转发或重定向的路径
             * 如果返回为null或者"",什么也不做
             * 判断返回值中是否存在冒号,如果没有默认转发,因为转发的情况较多
             * 如果有冒号,分割,得到前缀f表示forward,前缀r表示redirect
             * 后缀为要转发或重定向的路径
             */
            if (result == null || result.trim().isEmpty()) {
                return;
            }
            if (result.contains(":")) {  //"f:/index.jsp"
                int index = result.indexOf(":");
                String s = result.substring(0, index); //f
                String path = result.substring(index+1); // "/index.jsp"
                if (s.equalsIgnoreCase("r")) {
                    // "/192.168.11.24:8080/example/index.jsp"
                    if (path.contains(":")) { //有:说明有端口号是其他项目的路径
                        if (path.contains("http")) {
                            // "/http://192.168.11.24:8080/example/index.jsp"
                            response.sendRedirect(path.substring(1));
                        } else {
                            response.sendRedirect("http://"+path.substring(1));
                        }
                    } else {
                        response.sendRedirect(request.getContextPath()+path);
                    }
                } else if (s.equalsIgnoreCase("f")) {
                    request.getRequestDispatcher(path).forward(request, response);
                } else if (s.equalsIgnoreCase("d")) { //表示下载
                    /**
                     * 两个头一个流
                     * 1. Content-Type
                     * 2. Content-Disposition
                     * 3. 流:下载文件的数据
                     */
//                    path = "/WEB-INF/mp3/自娱自乐.mp3";
//                    path = "/G://a.jpg";
                    int indexOf = path.lastIndexOf("/");
                    String name = path.substring(indexOf); // /自娱自乐.mp3
                    //如果包含:,说明是绝对路径
                    String filename = null;
                    if (path.contains(":")) {
                        filename = path.substring(1);
                    } else { //说明是服务器端文件,需要获得绝对路径
                        //获得文件的绝对路径
                        filename = this.getServletContext().getRealPath(path);
                    }
                    //去掉文件名前的/
                    name = name.substring(1); // 自娱自乐.mp3
                    //通过DownUtils工具类处理不同浏览器下载时中文名乱码问题
                    String framename = DownUtils.filenameEncoding(name, request);
                    //头1:获得要下载的文件MIME类型
                    String contentType = this.getServletContext().getMimeType(filename);
                    //头2:ContentDisposition
                    String contentDisposition = "attachment;filename="+framename;
                    FileInputStream input = new FileInputStream(filename);
                    response.setHeader("Content-Type", contentType);
                    response.setHeader("Content-Disposition",contentDisposition);
                    ServletOutputStream output = response.getOutputStream();
                    IOUtils.copy(input, output);
                    input.close();
                    output.close();
                } else {
                    throw new RuntimeException("操作:"+s+"目前还不支持!");
                }
                
            } else { //默认转发
                request.getRequestDispatcher(result).forward(request, response);
            }
        } catch (Exception e) {
            throw new RuntimeException("调用的方法:"+methodName+"(HttpServletRequest,HttpServletResponse),内部抛出异常!", e);
        }
        
    }

}

DownUtils

/**
 * 文件下载时处理浏览器不同编码的中文乱码
 * @author hui.zhang
 * @date 2017-10-12 下午6:04:06
 */
public class DownUtils {
    public static String filenameEncoding(String filename, HttpServletRequest request) throws IOException{
        //获取浏览器头信息
        String agent = request.getHeader("User-Agent");
        if(agent.contains("Firefox")){
            BASE64Encoder base64Encoder = new BASE64Encoder();
            filename = "=?utf-8?B?"+base64Encoder.encode(filename.getBytes("utf-8"))+"?=";
        } else if(agent.contains("MSIE")){
            filename = URLEncoder.encode(filename,"utf-8");
        } else {
            filename = URLEncoder.encode(filename,"utf-8");
        }
        return filename;
    }

}

  代码中注释还算清楚,下载文件的功能是新加上的,测试还挺正常,如果使用出现问题欢迎提出!因为getParameter()在上传文件中失效,所以没写在Base中,后续也会传上来共同交流,感谢观看!

原文地址:https://www.cnblogs.com/stefan95/p/7667451.html