Java结合WebUploader文件上传(分块、断点续传)附源码下载

之前自己写小项目的时候也碰到过文件上传的问题,没有找到很好的解决方案。虽然之前网找各种解决方案的时候也看到过WebUploader,但没有进一步深究。这次稍微深入了解了些,这里也做个小结。
因为有很多人问我要源码,特附上源码下载链接在文章的末尾,供有需要时参考 -- 20171212 更新

一、 简单的文件和普通数据上传并保存

jsp页面:

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




Insert title here


    
文件:
信息:

servlet:

package com.yihengliu.web.action;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import javax.servlet.ServletException;
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.FileUtils;

/**
 * Servlet user to accept file upload
 */
public class FileUploadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    private String serverPath = "e:/";

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.getWriter().append("Served at: ").append(request.getContextPath());

        System.out.println("进入后台...");

        // 1.创建DiskFileItemFactory对象,配置缓存用
        DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();

        // 2. 创建 ServletFileUpload对象
        ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);

        // 3. 设置文件名称编码
        servletFileUpload.setHeaderEncoding("utf-8");

        // 4. 开始解析文件
        try {
            List items = servletFileUpload.parseRequest(request);
            for (FileItem fileItem : items) {

                if (fileItem.isFormField()) { // >> 普通数据
                    String info = fileItem.getString("utf-8");
                    System.out.println("info:" + info);
                } else { // >> 文件
                    // 1. 获取文件名称
                    String name = fileItem.getName();
                    // 2. 获取文件的实际内容
                    InputStream is = fileItem.getInputStream();

                    // 3. 保存文件
                    FileUtils.copyInputStreamToFile(is, new File(serverPath + "/" + name));
                }

            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}

二 、使用WebUploader组件上传

主要功能分为:分片、并发,预览、压缩,多途径添加文件夹(文件多选,拖拽等)、秒传

页面样式使用


        使用webuploader上传
        
        
        
        
        
        
         
         
文件上传

生成文件名列表、实时显示上传进度、显示缩略图

  • 增加文件列表div,
  • 生成缩略图和显示上传进度

    // 生成缩略图和上传进度
    uploader.on("fileQueued", function(file) {
            // 把文件信息追加到fileList的div中
            $("#fileList").append("
    " + file.name + "
    ") // 制作缩略图 // error:不是图片,则有error // src:代表生成缩略图的地址 uploader.makeThumb(file, function(error, src) { if (error) { $("#" + file.id).find("img").replaceWith("无法预览 "); } else { $("#" + file.id).find("img").attr("src", src); } }); } ); // 监控上传进度 // percentage:代表上传文件的百分比 uploader.on("uploadProgress", function(file, percentage) { $("#" + file.id).find("span.percentage").text(Math.round(percentage * 100) + "%"); });

拖拽上传、粘贴上传

  • 创建拖拽区域并设置样式:
            

    
    
  • 基本配置中增加dnd区域配置(开启拖拽)
    屏蔽拖拽区域外的响应
    开启粘贴功能
      var uploader = WebUploader.create(
        {
            swf:"${pageContext.request.contextPath }/js/Uploader.swf",
                 server:"${pageContext.request.contextPath }/FileUploadServlet",
            pick:"#filePicker",
            auto:true,
            // 开启拖拽
            dnd:"#dndArea",
            // 屏蔽拖拽区域外的响应
            disableGlobalDnd:true,
            // 
        }       
    );

文件的分块上传

前端根据需要发送的文件生成一个md5字符串发送给后台,后台创建以该md5字符串命名的文件夹。前端分块发送文件并发送文件块序号给后台,后台接收到文件后按序号名称保存。前端发送完成后通知后台合并文件。

  • 前端配置,开启是否分块、分块大小、线程个数等

    // 上传基本配置
    var uploader = WebUploader.create(
        {
            swf:"${pageContext.request.contextPath }/js/Uploader.swf",
            server:"${pageContext.request.contextPath }/FileUploadServlet",
            pick:"#filePicker",
            auto:true,
            dnd:"#dndArea",
            disableGlobalDnd:true,
            paste:"#uploader",
    
            // 分块上传设置
            // 是否分块
            chunked:true,
            // 每块文件大小(默认5M)
            chunkSize:5*1024*1024,
            // 开启几个并非线程(默认3个)
            threads:3,
            // 在上传当前文件时,准备好下一个文件
            prepareNextFile:true
        }       
    );
  • 前端监听分块
    可以分为三个时间点:
    • before-send-file: 该方法在文件上传前调用(只会在一个文件上传前调用)。
      可以在该方法中获取文件的md5字符串作为后台保存分块文件的目录名
    • before-send: 该方法在每个分块文件上传前调用(每个分块上传前都会调用)。
      可以在该方法中发送md5字符串到后台,后台判断是否已经存在分块决定是否发送以达到断点续传的功能
    • after-send-file: 该方法在所有文件上传完成没有错误之后调用(所有分块上传完成后调用)。
      可以在该方法中通知后台合并所有分块
  1. 前端获取文件md5字符串,发送每个分块时发送到后台,后台接收如果不存在文件夹创建文件夹,保存分块发送的文件
```javascript
// 监听分块上传的时间点,断点续传
var fileMd5;
WebUploader.Uploader.register({
    "before-send-file":"beforeSendFile",
    "before-send":"beforeSend",
    "after-send-file":"afterSendFile"
    },{
        beforeSendFile:function(file) {
            // 创建一个deffered,用于通知是否完成操作
            var deferred = WebUploader.Deferred();
            
            // 计算文件的唯一标识,用于断点续传和妙传
            (new WebUploader.Uploader()).md5File(file, 0, 5*1024*1024)
                .progress(function(percentage){
                    $("#"+file.id).find("span.state").text("正在获取文件信息...");
                })
                .then(function(val) {
                    fileMd5 = val;
                    $("#" + file.id).find("span.state").text("成功获取文件信息");
                    // 放行
                    deferred.resolve();
                });
            // 通知完成操作
            return deferred.promise();
        },
        beforeSend:function() {
            var deferred = WebUploader.Deferred();
            // 发送文件md5字符串到后台
            this.owner.options.formData.fileMd5 = fileMd5;
            deferred.resolve();
            return deferred.promise();
        },
        afterSendFile:function() {
            
        }
    }
);
```
  1. 添加state标签
```javascript
$("#fileList").append("
" + file.name + "
"); ```
  1. 保存文件
```java
// 4. 开始解析文件
// 文件md5获取的字符串
String fileMd5 = null;
// 文件的索引
String chunk = null;
try {
          List items = servletFileUpload.parseRequest(request);
        for (FileItem fileItem : items) {

            if (fileItem.isFormField()) { // >> 普通数据
                String fieldName = fileItem.getFieldName();
                if ("info".equals(fieldName)) {
                    String info = fileItem.getString("utf-8");
                    System.out.println("info:" + info);
                }
                if ("fileMd5".equals(fieldName)) {
                    fileMd5 = fileItem.getString("utf-8");
                    System.out.println("fileMd5:" + fileMd5);
                }
                if ("chunk".equals(fieldName)) {
                    chunk = fileItem.getString("utf-8");
                    System.out.println("chunk:" + chunk);
                }
            } else { // >> 文件
                // 如果文件夹没有创建文件夹
                File file = new File(serverPath + "/" + fileMd5);
                if (!file.exists()) {
                    file.mkdirs();
                }
                // 保存文件
                File chunkFile = new File(serverPath + "/" + fileMd5 + "/" + chunk);
                FileUtils.copyInputStreamToFile(fileItem.getInputStream(), chunkFile);
            }
        }
```    
  1. 前端通知action进行合并文件
    前端增加:
```javascript
// 通知合并分块
$.ajax(
    {
        type:"POST",
        url:"${pageContext.request.contextPath}/UploadActionServlet?action=mergeChunks",
        data:{
            fileMd5:fileMd5
        },
        success:function(response){
            
        }
    }
);
```
  1. 新增合并action:
```java
package com.yihengliu.web.action;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.UUID;

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

/**
 * 合并上传文件
 */
public class UploadActionServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    private String serverPath = "e:/";

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("进入合并后台...");
        String action = request.getParameter("action");
        if ("mergeChunks".equals(action)) {
            // 获得需要合并的目录
            String fileMd5 = request.getParameter("fileMd5");

            // 读取目录所有文件
            File f = new File(serverPath + "/" + fileMd5);
            File[] fileArray = f.listFiles(new FileFilter() {

                // 排除目录,只要文件
                @Override
                public boolean accept(File pathname) {
                    if (pathname.isDirectory()) {
                        return false;
                    }
                    return true;
                }
            });

            // 转成集合,便于排序
            List fileList = new ArrayList(Arrays.asList(fileArray));
            // 从小到大排序
            Collections.sort(fileList, new Comparator() {

                @Override
                public int compare(File o1, File o2) {
                    if (Integer.parseInt(o1.getName()) < Integer.parseInt(o2.getName())) {
                        return -1;
                    }
                    return 1;
                }

            });

            // 新建保存文件
            File outputFile = new File(serverPath + "/" + UUID.randomUUID().toString() + ".zip");

            // 创建文件
            outputFile.createNewFile();

            // 输出流
            FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
            FileChannel outChannel = fileOutputStream.getChannel();

            // 合并
            FileChannel inChannel;
            for (File file : fileList) {
                inChannel = new FileInputStream(file).getChannel();
                inChannel.transferTo(0, inChannel.size(), outChannel);
                inChannel.close();

                // 删除分片
                file.delete();
            }

            // 关闭流
            fileOutputStream.close();
            outChannel.close();

            // 清除文件加
            File tempFile = new File(serverPath + "/" + fileMd5);
            if (tempFile.isDirectory() && tempFile.exists()) {
                tempFile.delete();
            }

            System.out.println("合并文件成功");

        }
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}
```

断点续传

  • 前端页面发送前添加校验,校验是否已经上传分块

    beforeSend:function(block) {
      var deferred = WebUploader.Deferred();
    
      // 支持断点续传,发送到后台判断是否已经上传过
      $.ajax(
        {type:"POST",
        url:"${pageContext.request.contextPath}/UploadActionServlet?action=checkChunk",
        data:{
          // 文件唯一表示                             
          fileMd5:fileMd5,
          // 当前分块下标
          chunk:block.chunk,
          // 当前分块大小
          chunkSize:block.end-block.start
        },
        dataType:"json",
        success:function(response) {
          if(response.ifExist) {
            // 分块存在,跳过该分块
            deferred.reject();
          } else {
            // 分块不存在或不完整,重新发送
            deferred.resolve();
          }
        }
      }
      );
    
      // 发送文件md5字符串到后台
      this.owner.options.formData.fileMd5 = fileMd5;
      return deferred.promise();
    }
  • action中添加校验

     else if ("checkChunk".equals(action)) {
            // 校验文件是否已经上传并返回结果给前端
    
            // 文件唯一表示                               
            String fileMd5 = request.getParameter("fileMd5");
            // 当前分块下标
            String chunk = request.getParameter("chunk");
            // 当前分块大小
            String chunkSize = request.getParameter("chunkSize");
    
            // 找到分块文件
            File checkFile = new File(serverPath + "/" + fileMd5 + "/" + chunk);
    
            // 检查文件是否存在,且大小一致
            response.setContentType("text/html;charset=utf-8");
            if (checkFile.exists() && checkFile.length() == Integer.parseInt((chunkSize))) {
                response.getWriter().write("{\"ifExist\":1}");
            } else {
                response.getWriter().write("{\"ifExist\":0}");
            }
        }

源码下载

源码点击下载

转载于:https://www.cnblogs.com/liuchengcc/p/6643884.html

你可能感兴趣的:(Java结合WebUploader文件上传(分块、断点续传)附源码下载)