文件分片续传及文件类型判断

写在最前面

为了实现一个文件分片上传方法,找了很久的demo,但是一直没有找到,最后找到了一个plupload插件,可以支持文件分片。但是,第一次接触这个插件,有很多不会用的地方,网上的许多文章毕竟是他们做的,拿来当然会有许多坑,一步一步调试再理解插件的作用,最后成功。这个插件中,有许多功能,但我最主要的是用了插件的分片的功能,文件类型验证全部由后端验证。

关于plupload插件的使用方法

plupload插件的使用方法在csdn、博客园、开源中国论坛都有很多讲的,在这里给一个我觉得介绍挺完整的:https://www.cnblogs.com/2050/p/3913184.html
下面这个是官方文档:http://www.phpin.net/tools/plupload/

一、jsp页面



"uploader">
"listFile.jsp">下载

二、FileUpload类

  File dir = new File(request.getSession().getServletContext().getRealPath("/") + FileDir+"/"+userId);
                if(!dir.exists()){
                    //可创建多级目录,而mkdir()只能创建一级目录
                    dir.mkdirs();
                }
                //开始上传文件
                PluploadService.upload(plupload,fileName,dir);
                    System.out.println("上传成功,路径为:"+dir);
        }catch (Exception e){
            e.printStackTrace();
        }
        return "message";

三、PluloadService类

在这个类中我进行了文件类型判断,由于文件是分片上传的,服务器得到的文件是一块一块的,所以很难进行截取完整的文件头。因此,我多加了一个判断,文件是第一块的时候,截取文件头,获取文件类型从而进行判断。如果文件类型不允许,则直接返回。

 private static long UPLOAD_MAXSIZE = 800 * 1024 * 1024;

    public static void upload(Plupload plupload,String fileName, File pluploadDir) throws ServletException, IOException {
        String name = "" + System.currentTimeMillis()+fileName;
        uploads(plupload,name, pluploadDir);
    }

    /**
     * 上传方法
     * @param plupload  上传实体对象
     * @param pluploadDir 上传的文件夹名称
     * @param fileName 保存的文件名
     */
    private static void uploads(Plupload plupload,String fileName, File pluploadDir) {
        boolean flag=false;
        System.out.println(plupload);
        //用户上传文件被分隔的总块数
        int chunks = plupload.getChunks();
        //当前块,从0开始
        int nowChunk = plupload.getChunk();
        System.out.println("当前块:"+nowChunk+"\n");

        //这里Request请求类型的强制转换可能出错,配置文件中向SpringIOC容器引入multipartResolver对象即可。
        MultipartHttpServletRequest mulRequest = (MultipartHttpServletRequest) plupload.getRequest();
        //map中只有一个键值对
        MultiValueMap map = mulRequest.getMultiFileMap();
        if (map != null) {
            try {
                Iterator iterator = map.keySet().iterator();
                while (iterator.hasNext()) {
                    String key = iterator.next();
                    List multipartFileList = map.get(key);
                    //循环只进行一次
                    for (MultipartFile multipartFile : multipartFileList) {
                        //手动向Plupload对象传入MultipartFile属性值
                        plupload.setMultipartFile(multipartFile);
                        //新建目标文件,只有被流写入时才会真正存在
                        File targetFile = new File(pluploadDir + "/" + fileName);
                        //当前块为首块时,截取文件头进行判断
                        if(nowChunk==0){
                            if (!creatCheckFile(multipartFile, plupload.getRequest(),fileName)){
                                return;
                            }
                        }
                        //上传资料总块数大于1,要进行合并
                        if (chunks >1) {
                            File tempFile = new File(pluploadDir.getPath() + "/" + multipartFile.getName());
                            //第一块直接从头写入,不用从末端写入
                            savePluploadFile(multipartFile.getInputStream(), tempFile, nowChunk == 0 ? false : true);
                            //全部块已经上传完毕,此时targetFile因为有被流写入而存在,要改文件名字
                            if (chunks - nowChunk == 1) {
                                flag=true;
                                tempFile.renameTo(targetFile);
                            }
                        }else {
                            multipartFile.transferTo(targetFile);
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ServletException e) {
                e.printStackTrace();
            }
        }
    }
    public static boolean creatCheckFile(MultipartFile file, HttpServletRequest request,String fileName) throws ServletException, IOException {
        boolean flag = false;
        //获取文件名
        String message = "";
        //判断文件不为空
        if (file.getSize() != 0 && !file.isEmpty()) {
            flag = true;
            //判断文件大小
            if (file.getSize() <= UPLOAD_MAXSIZE) {
                flag = true;
                //文件类型判断
                if (CheckFileType.getCheckFile(file,fileName)) {
                    flag = true;
                } else {
                    flag = false;
                    System.out.println("文件格式不符合!");
                    message = "文件格式不符合";
                }
            } else {
                flag = false;
                System.out.println("文件大小超出范围!");
                message = "文件大小超出范围!";
            }
        } else {
            flag = false;
            System.out.println("文件为空!");
            message = "文件为空!";
        }
        return flag;
    }

    /**
     * 保存文件
     * @param is 文件输入流
     * @param tempFile 临时文件
     * @param flag 操作标识
     */
    private static void savePluploadFile(InputStream is, File tempFile, boolean flag){
        OutputStream os=null;
        try{
            //从末端写入
            if(tempFile.exists()){
                os=new BufferedOutputStream(new FileOutputStream(tempFile,true));
            }else {//从头写入
                os=new BufferedOutputStream(new FileOutputStream(tempFile));
            }
            byte []bytes=new byte[1024*1024*20];
            int len=0;
            while ((len=(is.read(bytes)))>0){
                os.write(bytes,0,len);
            }
            os.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try{
                os.close();
                is.close();
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }
}

四、文件类型判断

截取八位以上文件头,文件头会发生一点改变,截取八位文件头,部分文件的文件头是一样的。在这里截取的是前八位文件头。

/**
     * 判断上传的文件是否合法
     * 1.检查文件的扩展名
     * 2.检查文件的MIME类型
     *
     * @param file
     * @return boolean
     */
    public final static boolean getCheckFile(MultipartFile file, String fileName) {
        //为真表示符合上传条件,为假表标不符合
        boolean upflag = false;
        String sname = fileName.substring(fileName.lastIndexOf(".") + 1);
        //转换成小写
        sname = sname.toLowerCase();
        System.out.println("上传文件后缀:" + sname);
        //获取上传附件的文件头,判断属于哪种类型,并获取其扩展名
        byte[] b = new byte[4];
        try {
            InputStream is = file.getInputStream();
            is.read(b, 0, b.length);
            String fileHeader = getFileHeader(b);
            System.out.println("上传文件的文件头为:" + fileHeader);
            if (FILE_TYPE_MAP.containsKey(fileHeader)) {
                upflag = true;
                //检查文件扩展名
                String va = FILE_TYPE_MAP.get(fileHeader);
                System.out.println("文件的扩展名应为:" + va);
                if (va.equals(sname)) {
                    upflag = true;
                } else {
                //特殊情况校验,有的文件头前八位
                    if (elseCheck(sname,fileHeader)){
                        upflag=true;
                    }
                    //特殊情况校验,如果用户上传的扩展名为文本文件,则允许上传
                    if (sname.indexOf("txt") != -1) {
                        return true;
                    } else {
                        return false;
                    }

                }
                is.close();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return upflag;
    }

    /**
     * 获取上传文件类型
     *
     * @param b
     * @return
     */
    public final static String getFileHeader(byte[] b) {
        String value = getFileHexString(b);
        return value;
    }

    /**
     * 读取文件头,将字节数组的前四位转换成十六进制
     *
     * @param b 读取文件头信息的文件的byte数组
     * @return 文件头信息
     */
    public final static String getFileHexString(byte[] b) {
        StringBuilder builder = new StringBuilder();
        if (b == null || b.length <=0) {
            return null;
        }
        for (int i = 0; i < b.length; i++) {
            int v = b[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                builder.append(0);
            }
            builder.append(hv);
        }
        return builder.toString();
    }

    private static boolean elseCheck(String suffix,String fileHeader){
        boolean check=false;
        //扩展名为"wav"与"avi"的前八位文件头相同
        if ("wav".equals(suffix)&& "52494646".equals(fileHeader)){
            return true;
        }
        //扩展名为"f4v"与"flv"的文件头相同
        if ("f4v".equals(suffix) && "464c5601".equals(fileHeader)){
            return true;
        }
        //扩展名为"rmvb"与"rm"的文件头相同
        if ("rm".equals(suffix)&& "2e524d46".equals(fileHeader)){
            return true;
        }
        //扩展名为"asf"与"wmv"的文件头相同
        if ("asf".equals(suffix)&&"3026b275".equals(fileHeader)){
            return true;
        }
        //jar与zip只是前八位相同
        for (String s:wpsSame) {
            if ( suffix.equals(s)&& "504b0304".equals(fileHeader)){
                return true;
            }
        }
        for (String s:docSame) {
            if ( suffix.equals(s)&&"d0cf11e0".equals(fileHeader)){
                return true;
            }
        }
        return check;
    }

五、Plupload类

Plupload类名不能改变,除非在plupload源码插件中改变。在这里,没有给出允许文件类型的集合,这个可以自己定义。

public class Plupload {
    /**文件原名*/
    private String name;
    /**用户上传资料被分解总块数*/
    private int chunks = -1;
    /**当前块数(从0开始计数)*/
    private int chunk = -1;
    /**HttpServletRequest对象,不会自动赋值,需要手动传入*/
    private HttpServletRequest request;
    private HttpServletResponse response;
    /**保存文件上传信息,不会自动赋值,需要手动传入*/
    private MultipartFile multipartFile;

你可能感兴趣的:(实现,文件类型判断,分片续传,文件头判断文件类型)