1. 程式人生 > >【學習筆記】WebUploader+SpringMVC的實現方式

【學習筆記】WebUploader+SpringMVC的實現方式

因專案需使用上傳大檔案功能,因此在網路上尋找目前可以支援大檔案上傳的外掛。被推薦使用WebUploader。
目前研究了3天,寫一下自己的一些開發中的問題。
有些程式碼是網路直接貼上,並未做出處參考整理(因當時未儲存出處,如果誰遇到了可以聯絡我修改,謝謝)
使用的後端框架是SpringMVC

首先肯定要確定,需要用到的引入項(這裡用到的版本為webuploader-0.1.5)
這是下載的壓縮包中的檔案列表截圖

要引入到專案中的檔案在dist資料夾下
主要使用的檔案為以下3個
Uploader.swf
webuploader.css
webuploader.js
因為想使用整體的功能,未使用其他的刪除版本

<script type="text/javascript" src="<c:url value="/scripts/jquery-1.9.1.js"/>"></script>
<!--引入CSS-->
<link rel="stylesheet" href="<c:url value="/isdbs/demo/webupload/webuploader.css"/>" type="text/css" />
<!--引入JS-->
<script type="text/javascript" src="<c:url value="
/isdbs/demo/webupload/webuploader.js"/>
"></script>

jquery版本看自己使用的版本,未測試可以支援的最低版本

<div id="uploader" class="wu-example">
    <div class="btns">
        <!-- 這個picker是用來選擇檔案的 -->
        <div id="picker" style="display:inline"></div>
        <!-- 如果未使用自動上傳,點選按鈕可以觸發上傳事件 -->
<div id="ctlBtn" class="uploadbutton" >上傳</div> <!-- 存放需上傳的檔案資訊--> <div id="thelist" class="uploader-list"></div> </div> <!-- 上傳檔案的遮罩,本身未新增該div,因說到上傳檔案時,如果直接進行其他操作會有什麼影響,因此新增遮罩和提示資訊避免誤操作--> <div id="uploadbg"></div> <!-- 檔案上傳時的提示資訊處理,包含進度條等資訊--> <div id="uploadshow"> <font color='#EE7621'> <label class="state" id="waitUpload">等待上傳...</label> </font> <div class="progress" style="position: absolute;height:30px;width:300px;"> <p id="progressUpload" style="top:0px;position: absolute;transform: translate(0%,0%);font-size: 10px;height:15px;width:0px;background-color:#E0FFFF;text-align:center">0% </p> </div> </div> </div>

下面是兩個div遮罩樣式

#uploadbg {
    display: none;
    position: absolute;
    top: 0%;
    left: 0%;
    width: 100%;
    height: 100%;
    background-color: #EBEBEB;
    z-index: 1001;
    -moz-opacity: 0.7;
    opacity: .70;
    filter: alpha(opacity = 70);
}

#uploadshow {
    display: none;
    position: absolute;
    top: 45%;
    left: 30%;
    width: 40%;
    height: 10%;
    padding: 8px;
    border: 0px solid #F1F1F1;
    background-color: #F1F1F1;
    z-index: 1002;
    overflow: auto;
}

重點還是JS的處理

//遮罩的方法
function showdiv() {    
            $('#uploadbg').css("display","block");
            $('#uploadshow').css("display","block");
        }
function hidediv() {
            $('#uploadbg').css("display","none");
            $('#uploadshow').css("display","none");
        }

var $list = $("#thelist");//定義上傳檔案顯示的位置
var fileSize = '';//獲取檔案大小
var lastModiDate = '';//獲取檔案最後修改日期

//初始化Web Uploader
var uploader = WebUploader.create({
    swf: './demo/webupload/Uploader.swf',//swf檔案路徑
    //http://${header["host"]}${pageContext.request.contextPath} 該路徑為避免跨域錯誤出現使用。其他使用server處也需要這麼書寫
    server: 'http://${header["host"]}${pageContext.request.contextPath}/webuploader/uploadFile',// 檔案接收後臺路徑。
    pick: {
               id: '#picker',
               name:"multiFile",  //這個地方 name 沒什麼用,雖然開啟偵錯程式,input的名字確實改過來了。但是提交到後臺取不到檔案。如果想自定義file的name屬性,還是要和fileVal 配合使用。
               label: '選擇檔案',
               multiple:false   //預設為true,true表示可以多選檔案,HTML5的屬性
          },
    formData: {//上傳時提交自身需要的欄位並傳遞到後臺。
        "status":"file",
        "uploadNum":"0000004730",
        "existFlg":'false'
    },
    fileVal:'multiFile',
    resize: false,
    auto:true ,//是否自動上傳
    threads:3,//上傳併發數。允許同時最大上傳程序數。
    chunked: true,  //分片處理
    chunkSize: 10 * 1024 * 1024, //每片10M
    fileNumLimit:1,//最大選擇要上傳的檔案總數  
    disableGlobalDnd: true // 禁掉全域性的拖拽功能。 
});

//移除檔案的方法,未進行後端刪除的新增
function deleFile(fileId) {
    uploader.removeFile(fileId,true);//呼叫方法刪除檔案佇列
    $('#'+fileId).remove();//清理頁面元素
};

//上傳按鈕點選時觸發
$("#ctlBtn").on("click", function() {
    uploader.upload();
});

//當有檔案新增進來的時候
uploader.on( 'fileQueued', function( file ) {
    fileSize = file.size;//為檔案大小賦值
    var timestamp = Date.parse(new Date(file.lastModifiedDate));
    timestamp = timestamp / 1000;
    lastModiDate = timestamp;//獲取檔案最後修改時間,並轉換成時間戳
    $list.append( '<div id="' + file.id + '" class="item" style="display:inline">' +
        '<h4 class="info" style="display:inline">' + file.name + '</h4>&nbsp;&nbsp;<div class="uploadbutton" id=\'deleBut_'+file.id+'\' onclick="deleFile(\''+file.id+'\');" >移除</div>' +
    '</div>' );// 在檔案上傳的位置新增檔案資訊,可以修改
    showdiv();//因為我設定的是自動上傳,所以新增進來後,自動呼叫遮罩
});

//當某個檔案的分塊在傳送前觸發,主要用來詢問是否要新增附帶引數,大檔案在開起分片上傳的前提下此事件可能會觸發多次。
uploader.on('uploadBeforeSend', function (obj, data, headers) {
    data.lastModiDate = lastModiDate;//為formData賦值
    data.fileSize = fileSize;
});

//檔案上傳過程中建立進度條實時顯示。
uploader.on( 'uploadProgress', function( file, percentage ) {
    $progress = $('#waitUpload');
    $percent = $('#progressUpload');
    $progress.text('正在上傳,在此期間不要做其他操作,請耐心等待...');
    $percent.css( 'width', percentage * 100 + '%' );
    $percent.text((percentage * 100).toFixed(2) + '%');
});

//完成上傳,不管成功或者失敗,先把遮罩刪除。
uploader.on( 'uploadComplete', function( file ) {
    hidediv();
});

// 檔案上傳成功之後進行的處理。
uploader.on( 'uploadSuccess', function( file ,response  ) {
    data.tableName = 'FILE_TABLE';
    $success = $('#waitUpload');
    $success.text('上傳成功,正在合併檔案,請耐心等待...');
});

// 檔案上傳失敗,顯示上傳出錯。這個目前沒什麼用,因為我把提示資訊放在遮罩了,幾乎是閃現不見,所以該方法可以再次修改
uploader.on( 'uploadError', function( file ) {
    $error = $('#waitUpload');
    $error.text('上傳失敗,請移除檔案後重新嘗試上傳...');
});

//日期格式化成時間字串
Date.prototype.format = function(fmt) { 
    var o = { 
       "M+" : this.getMonth()+1,                 //月份 
       "d+" : this.getDate(),                    //日 
       "h+" : this.getHours(),                   //小時 
       "m+" : this.getMinutes(),                 //分 
       "s+" : this.getSeconds(),                 //秒 
       "q+" : Math.floor((this.getMonth()+3)/3), //季度 
       "S"  : this.getMilliseconds()             //毫秒 
   }; 
   if(/(y+)/.test(fmt)) {
           fmt=fmt.replace(RegExp.$1, (this.getFullYear()+"").substr(4 - RegExp.$1.length)); 
   }
    for(var k in o) {
       if(new RegExp("("+ k +")").test(fmt)){
            fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length)));
        }
    }
   return fmt; 
};  

上述即為前端使用的樣式、元素以及JS了。

下面來看看後端需要書寫的東西


    @RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, String> uploadFile(HttpServletRequest request, HttpServletResponse response) throws Exception {
        Map<String, String> sMap = new HashMap<String, String>();
        try {  
            boolean isMultipart = ServletFileUpload.isMultipartContent(request);  
            if (isMultipart) {  
                FileItemFactory factory = new DiskFileItemFactory();  
                ServletFileUpload upload = new ServletFileUpload(factory);  

                // 得到所有的提交的表單域,也就是formData  
                List<FileItem> fileItems = upload.parseRequest(request);  

                String id = "";  
                String fileName = "";  
                // 如果大於1說明是分片處理  
                int chunks = 1;  
                int chunk = 0;  
                long fileSize = 0;
                long lastModiDate = 0;
                String tableName = "";
                FileItem tempFileItem = null;  

                for (FileItem fileItem : fileItems) {  
                    if (fileItem.getFieldName().equals("id")) {  
                        id = fileItem.getString();  
                    } else if (fileItem.getFieldName().equals("name")) {  
                        fileName = new String(fileItem.getString().getBytes("ISO-8859-1"), "UTF-8");  
                    } else if (fileItem.getFieldName().equals("chunks")) {  
                        chunks = NumberUtils.toInt(fileItem.getString());  
                    } else if (fileItem.getFieldName().equals("chunk")) {  
                        chunk = NumberUtils.toInt(fileItem.getString());  
                    } else if (fileItem.getFieldName().equals("multiFile")) {  
                        tempFileItem = fileItem;  
                    } else if (fileItem.getFieldName().equals("fileSize")) {  
                        fileSize = NumberUtils.toLong(fileItem.getString());
                    } else if (fileItem.getFieldName().equals("lastModiDate")) {  
                        lastModiDate = NumberUtils.toLong(fileItem.getString());
                    } else if (fileItem.getFieldName().equals("tableName")) {  
                        tableName = fileItem.getString();
                    }
                }  

                System.out.println(tableName);
                String fileSysName = tempFileItem.getName();
                String realname = lastModiDate+fileSysName.substring(fileSysName.lastIndexOf("."));//轉化後的檔名  
                sMap.put("fileSysName", fileSysName);
                sMap.put("realname", realname);                              
                //真實上傳的地址,是我底層的地址,這裡就不多說了
                String realPath = Constants.ROOT_FILE_PATH;
                realPath = realPath + File.separator + tableName.toUpperCase() + File.separator;
                String filePath = realPath;//檔案上傳路徑  

                // 臨時目錄用來存放所有分片檔案  
                String tempFileDir = filePath + id + "_" + fileSize;
                File parentFileDir = new File(tempFileDir);  
                if (!parentFileDir.exists()) {  
                    parentFileDir.mkdirs();  
                }  
                // 分片處理時,前臺會多次呼叫上傳介面,每次都會上傳檔案的一部分到後臺  
                File tempPartFile = new File(parentFileDir, realname + "_" + chunk + ".part");  
                FileUtils.copyInputStreamToFile(tempFileItem.getInputStream(), tempPartFile);  

                // 是否全部上傳完成  
                // 所有分片都存在才說明整個檔案上傳完成  
                boolean uploadDone = true;  
                for (int i = 0; i < chunks; i++) {  
                    File partFile = new File(parentFileDir, realname + "_" + i + ".part");  
                    if (!partFile.exists()) {  
                        uploadDone = false;  
                    }  
                }  
                // 所有分片檔案都上傳完成  
                // 將所有分片檔案合併到一個檔案中  
                if (uploadDone) {  
                    // 得到 destTempFile 就是最終的檔案  
                    File destTempFile = new File(filePath, realname);  
                    for (int i = 0; i < chunks; i++) {  
                        File partFile = new File(parentFileDir, realname + "_" + i + ".part");  
                        FileOutputStream destTempfos = new FileOutputStream(destTempFile, true);  
                        //遍歷"所有分片檔案"到"最終檔案"中  
                        FileUtils.copyFile(partFile, destTempfos);  
                        destTempfos.close();  
                    }  
                    // 刪除臨時目錄中的分片檔案  
                    FileUtils.deleteDirectory(parentFileDir);  
                }   
            }  
        } catch (Exception e) {  
            logger.error(e.getMessage(), e);  
        }
        return sMap;
    }

上述即為SpringMVC的上傳方法了,目前已可以穩定上傳。