前段WebUploader;JavaEE大文件分片上传接收

Web大文件分片上传
Web环境中大文件上传不能再用form表单一次上传了,这样效率太低;
我在不断尝试SpringMVC环境下分片接受文件,最终失败;原因目测是 SpringMVC、Struts框架是不支持HTML5方式上传的(这类框架只能支持Form表单方式的文件上传,或者FLash)

那我们可以使用Servlet和SpringMVC结合集成方式实现大文件分片上传;

一、来看看我们的web.xml的配置
前段WebUploader;JavaEE大文件分片上传接收_第1张图片

很明显两个servlet,上面一个配置的是SpringMVC的入口,下面servlet是视频上传;
他们俩的url-pattern 不能冲突;

二、先来看看WebUploader 的前端代码
前段WebUploader;JavaEE大文件分片上传接收_第2张图片
以下是代码:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<html>
<head>
<base
    href="${pageContext.request.scheme}://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/">
<script type="text/javascript" src="webuploader/jquery-1.7.2.js">script>
<script type="text/javascript" src="webuploader/webuploader.min.js">script>
<link href="webuploader/webuploader.css" type="css/text" />
head>
<body>
    <h2>Hello World!h2>
    <div id="thelist" class="uploader-list">div>
    <div style="margin: 20px 20px 20px 0;">
        <div id="picker" class="form-control-focus">选择文件div>
    div>
    <button id="btnSync" type="button" class="btn btn-warning">开始同步button>

    <script>
        var uploader = WebUploader.create({

            // swf文件路径
            swf : 'webuploader/Uploader.swf',
            // 文件接收服务端。
            server : 'UploadVideoServlet',
            // 选择文件的按钮。可选。
            // 内部根据当前运行是创建,可能是input元素,也可能是flash.
            pick : '#picker',
            threads:2,
            chunked: true,  //分片处理
            chunkSize: 5 * 1024 * 1024, //每片5M  
            threads:1,//上传并发数。允许同时最大上传进程数。
            // 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传!
            resize : false
        });

        // 当有文件被添加进队列的时候
        uploader.on('fileQueued', function(file) {
            //alert(123);
            $("#thelist").append(
                    '
'" class="item">' + '

' + file.name + '

'
+ '

等待上传...

'
+ '
'
); }); uploader.on('uploadSuccess', function(file) { alert(uploader.options.formData.guid); alert(Math.ceil(file.size/(5*1024*1024))); alert(file.name); $('#' + file.id).find('p.state').text('已上传'); $.post("UploadSuccessServlet", { "guid": uploader.options.formData.guid,chunks:Math.ceil(file.size/(5*1024*1024)),fileName:file.name}, function(data){ }, "json"); }); uploader.on('uploadError', function(file) { $('#' + file.id).find('p.state').text('上传出错'); }); uploader.on('uploadComplete', function(file) { $('#' + file.id).find('.progress').fadeOut(); }); $("#btnSync").on('click', function() { if ($(this).hasClass('disabled')) { return false; } uploader.options.formData.guid = Math.random(); uploader.upload(); });
script> body> html>

三、servlet分片获取
分片就是前段将文件分成多个,每片都是一个post请求,有多少片就请求多少次servlet;
我们以获取的guid为文件名 建立临时文件夹,以chunk(片序号)为文件名来存储文件;
前段WebUploader;JavaEE大文件分片上传接收_第3张图片

以下为代码:

package com.airodlcx;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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

import org.apache.commons.beanutils.BeanUtils;
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.commons.io.FileUtils;

/**
 * Servlet implementation class UploadVideo
 */
public class UploadVideoServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public UploadVideoServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // TODO Auto-generated method stub
        response.getWriter().append("Served at: ").append(request.getContextPath());
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String path = request.getSession().getServletContext().getRealPath("/upload");
        System.out.println(path);
        DiskFileItemFactory factory = new DiskFileItemFactory();
        // 2、创建一个文件上传解析器
        ServletFileUpload upload = new ServletFileUpload(factory);
        // 解决上传文件名的中文乱码
        upload.setHeaderEncoding("UTF-8");
        // 3、判断提交上来的数据是否是上传表单的数据
        if (!ServletFileUpload.isMultipartContent(request)) {
            return;
        }
        // 4、使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List集合,每一个FileItem对应一个Form表单的输入项
        List list = null;
        try {
            list = upload.parseRequest(request);
        } catch (FileUploadException e) {
            e.printStackTrace();
        }

        HashMap map = new HashMap();

        System.out.println("-------------------------------------------------------------");
        for (FileItem item : list) {
            if (item.isFormField()) {
                /**
                 * 表单数据
                 */
                String name = item.getFieldName();
                // 解决普通输入项的数据的中文乱码问题
                String value = item.getString("UTF-8");
                // value = new String(value.getBytes("iso8859-1"),"UTF-8");
                System.out.println(name + "=" + value);
                map.put(name, value);// 放入map集合
            } else {
                /**
                 * 文件上传
                 */

                File fileParent = new File(path + "/" + map.get("guid"));//以guid创建临时文件夹
                System.out.println(fileParent.getPath());
                if (!fileParent.exists()) {
                    fileParent.mkdir();
                }


                String filename = item.getName();
                if (filename == null || filename.trim().equals("")) {
                    continue;
                }
                // 注意:不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的,如:
                // c:\a\b\1.txt,而有些只是单纯的文件名,如:1.txt
                // 处理获取到的上传文件的文件名的路径部分,只保留文件名部分
                filename = filename.substring(filename.lastIndexOf("\\") + 1);

                //创建文件
                File file;
                if (map.get("chunks") != null) {
                    file = new File(fileParent, map.get("chunk"));
                } else {
                    file = new File(fileParent, "0");
                }

                //copy
                FileUtils.copyInputStreamToFile(item.getInputStream(), file);

            }
        }
    }
}

四、前端WebUploader上传完毕触发uploadSuccess事件

uploader.on('uploadSuccess', function(file) {
            alert(uploader.options.formData.guid);
            alert(Math.ceil(file.size/(5*1024*1024)));
            alert(file.name);
            $('#' + file.id).find('p.state').text('已上传');
            $.post("UploadSuccessServlet", { "guid": uploader.options.formData.guid,chunks:Math.ceil(file.size/(5*1024*1024)),fileName:file.name},
               function(data){

            }, "json");
        });

请求servlet去合并之前的guid文件夹下的分片文件,post请求中的分片数量可以用来校验,获取的分片是否正确,也可以前端传递md5,后台校验;

五、后台获取的log

前段WebUploader;JavaEE大文件分片上传接收_第4张图片
前段WebUploader;JavaEE大文件分片上传接收_第5张图片
图:上传的GUID命名的文件夹
前段WebUploader;JavaEE大文件分片上传接收_第6张图片
图:文件夹下的分片文件
每个分割线包住的地方是一个servlet请求,最后在success 请求的servlet是进行文件校验并合并文件即可;代码见下:
六、java文件合并
代码见下:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String path = request.getSession().getServletContext().getRealPath("/upload");

        String guid = request.getParameter("guid");
        int chunks = Integer.parseInt(request.getParameter("chunks"));
        String fileName = request.getParameter("fileName");

        System.out.println("start...!guid="+guid+";chunks="+chunks+";fileName="+fileName);
        /**
         * 进行文件合并
         */
        File file = new File(path+"/"+guid);
        /**
         * 判断分片数量是否正确
         */
        if(file.list().length != chunks){
            return;
        }

        new File("F://upload"+"/"+guid).mkdir();
        /**
         * 进行文件合并
         */
        File newFile = new File("F://upload"+"/"+guid+"/"+fileName);
        FileOutputStream outputStream = new FileOutputStream(newFile, true);//文件追加写入

        byte[] byt = new byte[10*1024*1024];
        int len;
        FileInputStream temp = null;//分片文件
        for(int i = 0 ; inew FileInputStream(new File(path+"/"+guid+"/"+i));
            while((len = temp.read(byt))!=-1){
                System.out.println(len);
                outputStream.write(byt, 0, len);
            }
            temp.close();
        }
        /**
         * 当所有追加写入都写完  才可以关闭流
         */
        outputStream.close();
        //temp.close(); 写错了 移到for循环
        System.out.println("success!guid="+guid+";chunks="+chunks+";fileName="+fileName);
    }

文件夹以guid命名,数据库储存guid的名字,后期数据移动,只需要更改前端显示的路径;
在这里up遇到一个问题,上传报错:IOException:磁盘空间不足;然而我上传的磁盘还有20G;原因是系统盘空间不足;up重装系统就好了;

注:
1、进度条的显示就很容易了
引入bootstrap的进度条

<div class="progress">
      <div id="progress" class="progress-bar" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" style="width: 0%;">
        class="sr-only">60% Complete
      div>
div>

2.添加uploadProgress事件监听

uploader.on('uploadProgress', function(file,percentage) {
    $("#progress").css("width",parseInt(percentage*100)+"%");
});

percentage:当前上传的进度,小数,数值为1是上传完毕;
效果如下:
PS:UI简陋还请见谅!

源代码下载:http://pan.baidu.com/s/1nv93iqP 密码:78an

QQ:346640094 Email:[email protected];

你可能感兴趣的:(JavaEE)