通用上传Servlet

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通用上传Servlet相关的知识,希望对你有一定的参考价值。

java判断文件类型参考: http://blog.csdn.net/shixing_11/article/details/5708145

目前使用的项目结构是:spring+springmvc+mybatis.

文件上传需要考虑两个地方:

1. 文件上传的安全性,脚本等文件是可以通过更改文件的后缀来提交的,这也是大多数网站通过上传渗入的方式.

2. 对非保存的文件删除,上传的文件,如果操作人没有进行保存或其他操作,需要清除文件.

 

 本上传使用的包:

commons-fileupload
javax.servlet-api

我比较喜欢大段大段的上传,So,我就不客气啦~

 

package com.guoke.basic.servlet;

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.log4j.Logger;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;

/**
 * 通用文件上传servlet
 */
@SuppressWarnings("serial")
@WebServlet(urlPatterns = "/upload")
public class UploadServlet extends HttpServlet {
    /**
     * LOG4j 日志
     */
    protected final Logger LOG = Logger.getLogger(this.getClass());

    /**
     * 文件类型
     */
    protected final static Map<String, String> FILE_TYPE = new HashMap<>();

    /**
     * 初始化文件头部信息
     */
    static {
        getFileType();
    }

    /**
     * 常见文件头十六进制
     */
    protected static void getFileType() {
        FILE_TYPE.put("jpg", "FFD8FF"); //JPEG (jpg)
        FILE_TYPE.put("png", "89504E47");  //PNG (png)
        FILE_TYPE.put("gif", "47494638");  //GIF (gif)
        FILE_TYPE.put("tif", "49492A00");  //TIFF (tif)
        FILE_TYPE.put("bmp", "424D"); //Windows Bitmap (bmp)
        FILE_TYPE.put("dwg", "41433130"); //CAD (dwg)
        FILE_TYPE.put("html", "68746D6C3E");  //HTML (html)
        FILE_TYPE.put("rtf", "7B5C727466");  //Rich Text Format (rtf)
        FILE_TYPE.put("xml", "3C3F786D6C");
        FILE_TYPE.put("zip", "504B0304");
        FILE_TYPE.put("rar", "52617221");
        FILE_TYPE.put("psd", "38425053");  //Photoshop (psd)
        FILE_TYPE.put("eml", "44656C69766572792D646174653A");  //Email [thorough only] (eml)
        FILE_TYPE.put("dbx", "CFAD12FEC5FD746F");  //Outlook Express (dbx)
        FILE_TYPE.put("pst", "2142444E");  //Outlook (pst)
        FILE_TYPE.put("xls", "D0CF11E0");  //MS Word
        FILE_TYPE.put("doc", "D0CF11E0");  //MS Excel 注意:word 和 excel的文件头一样
        FILE_TYPE.put("mdb", "5374616E64617264204A");  //MS Access (mdb)
        FILE_TYPE.put("wpd", "FF575043"); //WordPerfect (wpd)
        FILE_TYPE.put("eps", "252150532D41646F6265");
        FILE_TYPE.put("ps", "252150532D41646F6265");
        FILE_TYPE.put("pdf", "255044462D312E");  //Adobe Acrobat (pdf)
        FILE_TYPE.put("qdf", "AC9EBD8F");  //Quicken (qdf)
        FILE_TYPE.put("pwl", "E3828596");  //Windows Password (pwl)
        FILE_TYPE.put("wav", "57415645");  //Wave (wav)
        FILE_TYPE.put("avi", "41564920");
        FILE_TYPE.put("ram", "2E7261FD");  //Real Audio (ram)
        FILE_TYPE.put("rm", "2E524D46");  //Real Media (rm)
        FILE_TYPE.put("mpg", "000001BA");  //
        FILE_TYPE.put("mov", "6D6F6F76");  //Quicktime (mov)
        FILE_TYPE.put("asf", "3026B2758E66CF11"); //Windows Media (asf)
        FILE_TYPE.put("mid", "4D546864");  //MIDI (mid)
    }


    /**
     * 处理post请求上传文件
     *
     * @param req HttpServletRequest对象
     * @param res HttpServletResponse 对象
     * @throws ServletException 异常处理
     * @throws IOException      异常处理
     */
    protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        PrintWriter out = res.getWriter();//声明输出打印流
        // 上传的文件路径
        String uploadPath = this.getServletContext().getRealPath(File.separator);
        String isRename = "";// 是否重命名 true:重命名
        //存放文件上传的临时目录路径
        String _tempPath = req.getServletContext().getRealPath(File.separator) + "temp";
        createFolder(_tempPath);//创建文件夹
        File tempPath = new File(_tempPath); // 用于存放临时文件的目录
        // 允许上传文件大小,最大上传文件,单位:字节 1000000/1024=0.9M
        int maxSize = 1000000;
        // 默认允许上传文件,建议设置允许上传的文件,方便
        String allowedFile = ".jpg,.gif,.png,.zip";
//        String deniedFile = ".exe,.com,.cgi,.asp"; // 默认不允许上传的文件
        //FileItem 对象的工厂,设定缓冲区大小和存放临时文件目录
        DiskFileItemFactory factory = new DiskFileItemFactory();
        // 允许设置内存中存储数据的门限,单位:字节
        factory.setSizeThreshold(4096);
        // 如果文件大小大于SizeThreshold,则保存到临时目录
        factory.setRepository(tempPath);
        //处理表单数据,将数据封装到 FileItem 对象中。处理上传的文件的数据,
        // 优先保存在缓冲区,如果数据超过了缓冲区大小,则保存到硬盘上,
        // 存储在DiskFileItemFactory指定目录下的临时文件。数据都接收完后,
        // 它再在从临时文件中将数据写入到上传文件目录下的指定文件中,并删除临时文件。
        ServletFileUpload upload = new ServletFileUpload(factory);

        try {
            //解析request请求
            List<FileItem> fileItems = upload.parseRequest(req);
            Iterator<FileItem> iterator = fileItems.iterator();
            String outPath = ""; //输出保存后的图片路径
            while (iterator.hasNext()) {
                //用来封装表单中的元素和数据。
                FileItem item = iterator.next();
                //获取name属性的值,是否存在上传路径
                if (item.getFieldName().equals("uploadPath")) {
                    outPath += item.getString();
                    uploadPath += outPath;
                    //获取name属性的值,是否重命名
                } else if (item.getFieldName().equals("isRename")) {
                    isRename = item.getString();
                    //获取最大上传文件大小
                } else if (item.getFieldName().equals("maxSize")) {
                    maxSize = Integer.parseInt(item.getString()) * 1048576;
                    //允许上传的文件类型
                } else if (item.getFieldName().equals("allowedFile")) {
                    allowedFile = item.getString();
//                } else if (item.getFieldName().equals("deniedFile")) {
//                    deniedFile = item.getString();
                } else if (!item.isFormField()) { // 忽略其他不是文件域的所有表单信息
                    String name = item.getName();//客户端的文件系统中的原始文件名
                    long size = item.getSize();//文件的大小,以字节为单位
                    //文件上传的类型
                    String fileTypeStr = name.substring(name.indexOf("."));
                    // 允许上传的文件类型
                    String[] allowFileType = allowedFile.split(",");
                    boolean isAllowFile = false;//是否是允许上传
                    for (String str : allowFileType) {
                        if (fileTypeStr.replaceAll("\\.", "").equals(str.replaceAll("\\.", ""))) {
                            isAllowFile = true;
                        }
                    }
                    if (!isAllowFile) {
                        //删除文件
                        tempPath.delete();
                    }

                    if ((name == null || name.equals("")) && size == 0)
                        continue;
                    try {
                        // 最大上传文件,单位:字节 1000000/1024=0.9M
                        upload.setSizeMax(maxSize);

                        // 保存上传的文件到指定的目录
                        // 在下文中上传文件至数据库时,将对这里改写
                        String fileName = System.currentTimeMillis() + fileTypeStr;
                        String savePath = uploadPath + File.separator;
                        //创建文件
                        createFolder(savePath);
                        // 重命名
                        if (isBlank(isRename) || Boolean.parseBoolean(isRename)) {
                            savePath += fileName;
                            outPath += fileName;
                        } else {
                            savePath += name;
                            outPath += name;
                        }
                        File newFile = new File(savePath);
                        item.write(newFile);//把上传的内容写到一个文件中
                        out.print(outPath.trim());
                        //对比文件上传类型是否和实际类型不相等
                        if (!getFileType(newFile).equals(fileTypeStr.replaceAll("\\.", ""))) {
                            //删除文件
                            newFile.delete();
                            LOG.debug("file is delete");
                        } else {
                            LOG.debug("upload file ok return path " + outPath);
                        }
                        out.flush();
                        out.close();
                    } catch (Exception e) {
                        this.LOG.debug(e);
                    }
                }
            }
        } catch (FileUploadException e) {
            this.LOG.debug(e);
        }
    }

    /**
     * 根据路径创建文件或文件夹
     * @param path 文件路径
     * @return 返回boolean值,true创建成功,false创建失败
     */
    public static boolean createFolder(String path) {
        File file = new File(path);
        return !file.exists() && file.mkdirs();
    }

    /**
     * 判断对象是否等于null,如果为字符串则还需要判断是否等于“”或“ ”
     * @param obj string
     * @return if is blank,return true, else ,return false
     */
    public static boolean isBlank(Object obj){
        //如果是字符串类型,则先清除前后空格符
        if(obj instanceof String){
            obj=((String) obj).trim();
        }
        //判断对象是否为null  hash判断 java7提供!
        return Objects.hash(obj) == 0;
    }

    /**
     * 获取文件头类型
     *
     * @param file 文件
     * @return 文件头部类型字符串
     */
    protected String getFileType(File file) {
        String fileType = null;
        byte[] b = new byte[50];
        if (file.exists()) {
            try {
                //
                InputStream is = new FileInputStream(file);
                //从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
                is.read(b);
                //获取文件类型
                fileType = getFileTypeByStream(b);
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return fileType;
    }

    /**
     * 从文件的数据流中对比文件头部信息
     *
     * @param b 字节数组
     * @return 文件头
     */
    protected String getFileTypeByStream(byte[] b) {
        //将字节数组转成字符串
        String filetypeHex = String.valueOf(getFileHexString(b));
        //迭代map
        Set<Map.Entry<String, String>> entrySet = FILE_TYPE.entrySet();
        for(Map.Entry<String, String> entry:entrySet){
            //判断字符串的开头是否包含对应的文件头
            if (filetypeHex.toUpperCase().startsWith(entry.getValue())) {
                return entry.getKey();
            }
        }
        return null;
    }

    /**
     * 将十六进制字节转成字符串
     *
     * @param byteArray 字节数组
     * @return 转换后的字符串
     */
    protected static String getFileHexString(byte[] byteArray) {
        //单线程比StringBuffer 要快
        StringBuilder stringBuilder = new StringBuilder();
        if (byteArray == null || byteArray.length <= 0) {
            return null;
        }
        for (byte b : byteArray) {
            int v = b & 0xFF; //将原来的负值变成正值
            //以十六进制(基数 16)无符号整数形式返回一个整数参数的字符串表示形式。
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString();
    }

}

 

以上是关于通用上传Servlet的主要内容,如果未能解决你的问题,请参考以下文章

Eclipse 中的通用代码片段或模板

Tomcat根据JSP生成Servlet机制解析

java Ftp上传创建多层文件的代码片段

servlet3.0的文件上传代码配置怎么写

在servlet中怎样上传文件?

使用超链接和后台Servlet实现文件上传和下载(附图和代码)