WebUploader文件上傳

之前自己寫小項目的時候也碰到過文件上傳的問題,沒有找到很好的解決方案。雖然之前網找各種解決方案的時候也看到過WebUploader,但沒有進一步深究。這次稍微深入瞭解了些,這裏也做個小結。

簡單的文件和普通數據上傳並保存

jsp頁面:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    <form action="${pageContext.request.contextPath }/FileUploadServlet" method="post" enctype="multipart/form-data">
        文件:<input type="file" value="請選擇文件" name="file" /> <br/>
        信息:<input type="text" name="info" /> <br/>
        <input type="submit" value="提交" />
    </form>
</body>
</html>

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<FileItem> 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組件上傳

分片、併發,預覽、壓縮,多途徑添加文件夾(文件多選,拖拽等),妙傳

  • 頁面樣式使用

    <html>
    <title>使用webuploader上傳</title>
    <!-- 1.引入文件 -->
    <link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath }/js/webuploader.css">
    <script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-2.1.4.min.js"></script>
    <script type="text/javascript" src="${pageContext.request.contextPath }/js/webuploader.js"></script>
    </head>
    <body>
        <!-- 2.創建頁面元素 -->
        <div id="upload">
            <div id="filePicker">文件上傳</div>
        </div>
        <!-- 3.添加js代碼 -->
        <script type="text/javascript">
            var uploader = WebUploader.create(
                {
                    swf:"${pageContext.request.contextPath }/js/Uploader.swf",
                    server:"${pageContext.request.contextPath }/FileUploadServlet",
                    pick:"#filePicker",
                    auto:true
                }       
            );
        </script>
    </body>
    </html>
  • 生成文件名列表、實時顯示上傳進度、顯示縮略圖
    • 增加文件列表div, <div id="fileList"></div>
    • 生成縮略圖和顯示上傳進度
    // 生成縮略圖和上傳進度
    uploader.on("fileQueued", function(file) {
            // 把文件信息追加到fileList的div中
            $("#fileList").append("<div id='" + file.id + "'><img/><span>" + file.name + "</span><div><span class='percentage'><span></div></div>")
    
            // 製作縮略圖
            // error:不是圖片,則有error
            // src:代表生成縮略圖的地址
            uploader.makeThumb(file, function(error, src) {
                if (error) {
                    $("#" + file.id).find("img").replaceWith("<span>無法預覽&nbsp;</span>");
                } 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) + "%");
    });
  • 拖拽上傳、粘貼上傳
    • 創建拖拽區域並設置樣式:

      <style type="text/css">
          #dndArea {
              width: 200px;
              height: 100px;
              border-color: red;
              border-style: dashed;
          }
      </style>        
      
      <!-- 創建用於拖拽的區域 -->
      <div id="dndArea"></div>
    • 基本配置中增加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: 該方法在所有文件上傳完成沒有錯誤之後調用(所有分塊上傳完成後調用)。
        可以在該方法中通知後臺合併所有分塊
    • 前端獲取文件md5字符串,發送每個分塊時發送到後臺,後臺接收如果不存在文件夾創建文件夾,保存分塊發送的文件

          // 監聽分塊上傳的時間點,斷點續傳
      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() {
      
              }
          }
      );

      添加state標籤

      $("#fileList").append("<div id='" + file.id + "'><img/><span>" + file.name + "</span><div><span class='state'></span></div><div><span class='percentage'></span></div></div>");

      保存文件

      // 4. 開始解析文件
      // 文件md5獲取的字符串
      String fileMd5 = null;
      // 文件的索引
      String chunk = null;
      try {
          List<FileItem> 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 { // >> 文件
                  /*// 1. 獲取文件名稱
                  String name = fileItem.getName();
                  // 2. 獲取文件的實際內容
                  InputStream is = fileItem.getInputStream();
      
                  // 3. 保存文件
                  FileUtils.copyInputStreamToFile(is, new File(serverPath + "/" + name));*/
      
                  // 如果文件夾沒有創建文件夾
                  File file = new File(serverPath + "/" + fileMd5);
                  if (!file.exists()) {
                      file.mkdirs();
                  }
                  // 保存文件
                  File chunkFile = new File(serverPath + "/" + fileMd5 + "/" + chunk);
                  FileUtils.copyInputStreamToFile(fileItem.getInputStream(), chunkFile);
      
              }
      
          }
    • 前端通知action進行合併文件
      前端增加:

    // 通知合併分塊
    $.ajax(
        {
            type:"POST",
            url:"${pageContext.request.contextPath}/UploadActionServlet?action=mergeChunks",
            data:{
                fileMd5:fileMd5
            },
            success:function(response){
    
            }
        }
    );
    新增合併action:
    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<File> fileList = new ArrayList<File>(Arrays.asList(fileArray));
                // 從小到大排序
                Collections.sort(fileList, new Comparator<File>() {
    
                    @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}");
            }
        }
發佈了5 篇原創文章 · 獲贊 5 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章