1. 程式人生 > >H5+JAVA的檔案上傳,斷點續傳

H5+JAVA的檔案上傳,斷點續傳

  • 斷點上傳能夠防止意外情況導致上傳一半的檔案下次上傳時還要從頭下載,網上有很多關於斷點的實現,這篇文章只是從前到後完整的記錄下一個可用的例項,由於生產環境要求不高,而且就是提供給一兩個人用,所以我簡化了諸多過程,不用flash,也不用applet,只是通過html5的新特性進行瀏覽器端的處理。
  • 簡單說下關鍵點
  1. 如果上次傳到n位元組,那麼瀏覽器下次續傳直接就是從檔案的n位元組開始向伺服器傳送資料,而不是都傳過去,伺服器從n位元組開始接收。
  2. html5能給檔案分片,所以每次上傳完一塊檔案後,應該返回當前已經上傳的檔案大小,好讓h5能從此斷點繼續讀取。
  3. 前端的js是網上別人的底子,我進行了可用性修改。
  4. 程式碼完全可用,而且都是用的最簡單的東西實現
  5. 可以看到我用了本地檔案的最後修改時間這個屬性,因為這樣可以脫離資料庫只通過檔名+檔案最後修改時間來確定檔案的唯一性,如果生產中有資料庫的接入,建議先生成續傳檔案並返回對應的唯一id。
  • 伺服器端方法
  1. 獲取當前已經上傳檔案的大小
複製程式碼
     /**
      * 獲取已上傳的檔案大小
      * @param request
      * @param response
      */
     public void getChunkedFileSize(HttpServletRequest request,HttpServletResponse response){
//儲存檔案的路徑,根據自己實際確定 String currentFilePath = "c:\\uploadFile\\Image\\"; PrintWriter print = null; try { request.setCharacterEncoding("utf-8"); print = response.getWriter(); String fileName = new String(request.getParameter("fileName").getBytes("ISO-8859-1"),"UTF-8");
String lastModifyTime = request.getParameter("lastModifyTime"); File file = new File(currentFilePath+fileName+"."+lastModifyTime); if(file.exists()){ print.print(file.length()); }else{ print.print(-1); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }
複製程式碼

2.檔案上傳

複製程式碼
/**
     * 
     * 斷點檔案上傳 1.先判斷斷點檔案是否存在 2.存在直接流上傳 3.不存在直接新建立一個檔案 4.上傳完成以後設定檔名稱
     *
     */
    public static void appendUpload2Server(HttpServletRequest request,HttpServletResponse response) {
        PrintWriter print = null;
        try {
            request.setCharacterEncoding("utf-8");
            print = response.getWriter();
            String fileSize = request.getParameter("fileSize");
            long totalSize = StringUtil.toLong(fileSize);
            RandomAccessFile randomAccessfile = null;
            long currentFileLength = 0;// 記錄當前檔案大小,用於判斷檔案是否上傳完成
            String currentFilePath = "c:\\uploadFile\\Image\\";// 記錄當前檔案的絕對路徑
            String fileName = new String(request.getParameter("fileName").getBytes("ISO-8859-1"),"UTF-8");
            String lastModifyTime = request.getParameter("lastModifyTime");
            File file = new File(currentFilePath+fileName+"."+lastModifyTime);
            // 存在檔案
            if(file.exists()){
                randomAccessfile = new RandomAccessFile(file, "rw");
            }
             else {
                // 不存在檔案,根據檔案標識建立檔案
                randomAccessfile = new RandomAccessFile(currentFilePath+fileName+"."+lastModifyTime, "rw");
            }
                    // 開始檔案傳輸
                InputStream in = request.getInputStream();
                randomAccessfile.seek(randomAccessfile.length());
                byte b[] = new byte[1024];
                int n;
                while ((n = in.read(b)) != -1) {
                    randomAccessfile.write(b, 0, n);
                }

            currentFileLength = randomAccessfile.length();

            // 關閉檔案
            closeRandomAccessFile(randomAccessfile);
            randomAccessfile = null;
            // 整個檔案上傳完成,修改檔案字尾
            if (currentFileLength == totalSize) {
                    File oldFile = new File(currentFilePath+fileName+"."+lastModifyTime);
                    File newFile = new File(currentFilePath+fileName);
                    if(!oldFile.exists()){
                        return;//重新命名檔案不存在
                    }
                    if(newFile.exists()){// 如果存在形如test.txt的檔案,則新的檔案儲存為test+當前時間戳.txt, 沒處理不帶副檔名的檔案 
                        String newName = fileName.substring(0,fileName.lastIndexOf("."))
                                +System.currentTimeMillis()+"."
                                +fileName.substring(fileName.lastIndexOf(".")+1);
                        newFile = new File(currentFilePath+newName);
                    }
                    if(!oldFile.renameTo(newFile)){
                        oldFile.delete();
                    }
                     
            }
            print.print(currentFileLength);
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 關閉隨機訪問檔案
     * 
     * @param randomAccessfile
     */
    public static void closeRandomAccessFile(RandomAccessFile rfile) {
        if (null != rfile) {
            try {
                rfile.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
複製程式碼
  • jsp頁面
複製程式碼
<html>
<head>
    <title>斷點續傳檔案</title>
    <meta charset="utf-8">
</head>
 
<body onload="init();">
<div class="row">
      <label for="fileToUpload">請選擇需要上傳的檔案</label>
      <input type="file" name="fileToUpload" id="fileToUpload" onchange="fileSelected();" multiple/>
</div>

</div>
<div class="row">
    <button onclick="uploadFiles()">上傳</button>
    <button onclick="pauseUpload()">暫停</button>
    &nbsp;<label id="progressNumber"></label>
</div>
<div id="msg" style="max-height: 400px; overflow:auto;min-height: 100px;">
</div>
<br>
<div><h6>支援批量,支援斷點續傳</h6></div>
</body>
</html>
複製程式碼
  • js程式碼
複製程式碼
var msg = null;
var paragraph = 1024*1024*2;  //每次分片傳輸檔案的大小 2M
var blob = null;//  分片資料的載體Blob物件
var fileList = null; //傳輸的檔案
var uploadState = 0;  // 0: 無上傳/取消, 1: 上傳中, 2: 暫停
 
//初始化訊息框
function init(){
    msg = document.getElementById("msg");
}
function uploadFiles(){
     //將上傳狀態設定成1
    uploadState = 1;
    if(fileList.files.length>0){
        for(var i = 0; i< fileList.files.length; i++){
            var file = fileList.files[i];
            uploadFileInit(file,i);
        }
    }else{
        msg.innerHTML = "請選擇上傳檔案!";
    }
}
/**
 * 獲取伺服器檔案大小,開始續傳
 * @param file
 * @param i
 */
function uploadFileInit(file,i){
    if(file){
        var startSize = 0;
        var endSize = 0;
        var date = file.lastModifiedDate;
        var lastModifyTime = date.getFullYear()+"-"+(date.getMonth()+1)+"-"+date.getDate()+"-"
        +date.getHours()+"-"+date.getMinutes()+"-"+date.getSeconds()
        //獲取當前檔案已經上傳大小
        jQuery.post("xxx/getChunkedFileSize.do",
                {"fileName":encodeURIComponent(file.name),"fileSize":file.size,"lastModifyTime":lastModifyTime,"chunkedFileSize":"chunkedFileSize"},
                function(data){
                    if(data != -1){
                        endSize = Number(data);
                    }
                    uploadFile(file,startSize,endSize,i);
            
        });
         
    }
}
/**
 * 分片上傳檔案
 */
function uploadFile(file,startSize,endSize,i) {
        var date = file.lastModifiedDate;
        var lastModifyTime = date.getFullYear()+"-"+(date.getMonth()+1)+"-"+date.getDate()+"-"
        +date.getHours()+"-"+date.getMinutes()+"-"+date.getSeconds()
        var reader = new FileReader();
        reader.onload = function loaded(evt) {
            // 構造 XMLHttpRequest 物件,傳送檔案 Binary 資料
            var xhr = new XMLHttpRequest(); 
                xhr.sendAsBinary = function(text){
                    var data = new ArrayBuffer(text.length);
                    var ui8a = new Uint8Array(data, 0);
                    for (var i = 0; i < text.length; i++) ui8a[i] = (text.charCodeAt(i) & 0xff);
                    this.send(ui8a);
                }

            xhr.onreadystatechange = function(){
                if(xhr.readyState==4){
                    //表示伺服器的相應程式碼是200;正確返回了資料   
                   if(xhr.status==200){ 
                       //純文字資料的接受方法   
                       var message=xhr.responseText; 
                       message = Number(message);
                       uploadProgress(file,startSize,message,i);
                    } else{
                        msg.innerHTML = "上傳出錯,伺服器相應錯誤!";
                    }  
               }  
            };//建立回撥方法
            xhr.open("POST", 
                    "xxx/appendUpload2Server.do?fileName=" + encodeURIComponent(file.name)+"&fileSize="+file.size+"&lastModifyTime="+lastModifyTime,
                    false); 
            xhr.overrideMimeType("application/octet-stream;charset=utf-8"); 
            xhr.sendAsBinary(evt.target.result); 
        };
        if(endSize < file.size){
            //處理檔案傳送(位元組)
            startSize = endSize;
            if(paragraph > (file.size - endSize)){
                endSize = file.size;
            }else{
                endSize += paragraph ;
            }
            if (file.webkitSlice) {
              //webkit瀏覽器
                blob = file.webkitSlice(startSize, endSize);
            }else
                blob = file.slice(startSize, endSize);
            reader.readAsBinaryString(blob);
        }else{
            document.getElementById('progressNumber'+i).innerHTML = '100%';
        }
}
 
//顯示處理程序
function uploadProgress(file,startSize,uploadLen,i) {
    var percentComplete = Math.round(uploadLen * 100 / file.size);
    document.getElementById('progressNumber'+i).innerHTML = percentComplete.toString() + '%';
    //續傳
    if(uploadState == 1){
        uploadFile(file,startSize,uploadLen,i);
    }
}
 
/*
暫停上傳
*/
function pauseUpload(){
    uploadState = 2;
}

/**
 * 選擇檔案之後觸發事件
 */
function fileSelected() {
    fileList = document.getElementById('fileToUpload');
    var length = fileList.files.length;
    var frame = document.getElementById('fileFrame');
        for(var i=0; i<length; i++){
            file = fileList.files[i];
            if(file){
                var fileSize = 0;
                if (file.size > 1024 * 1024)
                    fileSize = (Math.round(file.size * 100 / (1024 * 1024)) / 100).toString() + 'MB';
                else
                    fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + 'KB';
                var nameDiv = document.createElement("div");
                    nameDiv.setAttribute("id","fileName"+i);
                    nameDiv.innerHTML='Name: ' + file.name;
                var sizeDiv = document.createElement("div");
                    sizeDiv.setAttribute("id","fileSize"+i);
                    sizeDiv.innerHTML='fileSize: ' + fileSize;
                var typeDiv = document.createElement("div");
                    typeDiv.setAttribute("id","progressNumber"+i);
                    typeDiv.innerHTML='';
            }
            frame.appendChild(nameDiv);
            frame.appendChild(sizeDiv);
            frame.appendChild(typeDiv);
        }
}
複製程式碼