使用Webuploader大文件分片傳輸

背景:50G大文件的HTTP上傳至服務器。php

好了,根據這個命題,能夠開始研究咱們怎麼作才能把這麼大的文件上傳成功。css

分片上傳是確定的,斷點續傳也是要有的,進度可視化那就更好了,基於這些,我選擇了Webuploader在前端進行分片上傳。html

爲何選擇它呢,好吧,它簡單,易上手,好排錯,文檔多......前端

實際是我懶......git

網上的教程大部分是複製粘貼,借鑑起來也很無奈,推薦一個我以爲比較實在的github

http://www.javashuo.com/article/p-zuncciyj-cg.htmlweb

本篇Demo地址,歡迎各位大佬指點json

https://github.com/papapalh/Big_File數組

 

一:開始promise

新創建項目,這裏用了php7.0版本後臺處理。

沒啥說的,下載WebUploader的包http://fex.baidu.com/webuploader/download.html

jQuery也是必須的,由於就是依賴jQ的。

好了,能夠初始化咱們的上傳組件了,介紹一下這裏Demo的配置

// 建立上傳
var uploader = WebUploader.create({
    swf: '/webuploader-0.1.5/Uploader.swf',
    server: 'index.php',     // 服務端地址
    pick: '#picker',         // 指定選擇文件的按鈕容器
    resize: false,
    chunked: true,           //開啓分片上傳
    chunkSize: 1024*1024*4,  //每一片的大小
    chunkRetry: 100,         // 若是遇到網絡錯誤,從新上傳次數
    threads: 3,              //上傳併發數。容許同時最大上傳進程數。
});
// 上傳提交
$("#ctlBtn").click(function () {
    console.log('準備上傳...');
    uploader.upload();
});

二:上傳分片

好了,這樣看吧,一個文件會被切分紅爲若干個小片斷髮送到服務器中。

可是,咱們以後要作的斷點續傳,如何以惟一的標識來記錄這個文件呢。

用MD5吧,簡單粗暴,我以爲確定有更好的辦法,可是因爲是DEMO,先總體跑下來在說。

下面這段代碼作了這些事

  1. 添加文件進來時計算文件的MD5,用於文件的惟一標識
  2. 檢查以前有沒有上傳一半出問題的,若是出問題了,那麼,以MD5命名的文件夾確定會有,那麼咱們傳以後的就行了。
  3. 我給這個狀態綁定了一個參數Status
// 當有文件被添加進隊列的時候-md5序列化
uploader.on('fileQueued', function (file) {
console.log(
"正在計算MD5值..."); uploader.md5File(file) .then(function (fileMd5) { file.wholeMd5 = fileMd5; file_md5 = fileMd5; console.log("MD5計算完成。"); console.log("正在查找有無斷點..."); $.post('check.php', {md5: file_md5}, function (data) { data = JSON.parse(data); switch (data.code) { // 斷點 case '0': console.log('有斷點.正在準備從斷點處上傳文件。'); for (var i in data.block_info) { block_info.push(data.block_info[i]); } file.status = 0; break; // 無斷點 case '1': console.log('無斷點.上傳新文件。'); file.status = 1; break; } }) }); });

check.php

檢查有沒有遺留的文件夾,有的話說明你以前上傳過,這些我就不要了,並返回上傳成功的分片 JSON

<?php
    // 接收相關數據
    $post = $_POST;

    // 找出分片文件
    $dir = '/var/www/'.$post['md5'];

    // 有斷點
    if (file_exists($dir)) {
        // 找出上傳成功的全部文件
        $block_info=scandir($dir);

        // 除去無用文件
        foreach ($block_info as $key => $block) {
            if ($block == '.' || $block == '..') unset($block_info[$key]);
        }

        echo json_encode(["code"=>"0" , 'block_info' => $block_info]);
    }
    // 無斷點
    else {
        echo json_encode(["code"=>"1"]);
    }

index.php

接受傳入文件,寫入臨時文件,這裏其實也應該用個MD5來檢查分片

<?php
    // 接收相關數據
    $post = $_POST;
    $file = $_FILES;
    $status = $post['status'];

    // 創建臨時目錄存放文件-以MD5爲惟一標識
    $dir = "/var/www/" . $post['md5value'];

    // 斷點上傳
    if ($status == '0') {
        // 獲取分片文件內容
        $block_info=scandir($dir);
        // 除去無用文件
        foreach ($block_info as $key => $block) {
            if ($block == '.' || $block == '..') unset($block_info[$key]);
        }
    }
    // 直接上傳
    elseif($status == '1') {
        if (!file_exists($dir)) {
            mkdir ($dir,0777,true);
        }

        // 移入緩存文件保存
        move_uploaded_file($file["file"]["tmp_name"], $dir.'/'.$post["chunk"]);
    }

 

三:斷點.跳過已有分片

這個地方是困擾了我很長時間的地方

官方API對於跳過度片的內容也找不到,因此單獨把他拿出來,往後也方便查看

剛剛咱們把若是有斷點的,咱們把上傳成功的分片數組拿出來,比對一下,若是有,就不上傳了

// 發送前檢查分塊,並附加MD5數據
uploader.on('uploadBeforeSend', function( block, data ) {
    var file = block.file;
    var deferred = WebUploader.Deferred();  

    data.md5value = file.wholeMd5;
    data.status = file.status;

    if ($.inArray(block.chunk.toString(), block_info) >= 0) {
        console.log("已有分片.正在跳過度片"+block.chunk.toString());
        deferred.reject();  
        deferred.resolve();
        return deferred.promise();
    }
});

這樣就完成了咱們對於斷點和分片的處理

 

四:合併

首先你得告訴我,你上傳完了,該合併了

// 上傳完成後觸發
uploader.on('uploadSuccess', function (file,response) {
    console.log("上傳分片完成。");
    console.log("正在整理分片...");
    $.post('merge.php', { md5: file.wholeMd5, fileName: file.name }, function (data) {
        var object = JSON.parse(data);
        if (object.code) {
            console.log("上傳成功");
        }
    });
});

這是Webuploader它上傳成功的一個回調

告訴了merge.php

讓開吧,我要合併了,就這個意思吧

<?php
    // 接收相關數據
    $post = $_POST;

    // 找出分片文件
    $dir = '/var/www/'.$post['md5'];

    // 獲取分片文件內容
    $block_info = scandir($dir);

    // 除去無用文件
    foreach ($block_info as $key => $block) {
        if ($block == '.' || $block == '..') unset($block_info[$key]);
    }

    // 數組按照正常規則排序
    natsort($block_info);

    // 定義保存文件
    $save_file = "/var/www/".$post['fileName'];

    // 沒有?創建
    if (!file_exists($save_file)) fopen($post['fileName'], "w");

    // 開始寫入
    $out = @fopen($save_file, "wb");

    // 增長文件鎖
    if (flock($out, LOCK_EX)) {
        foreach ($block_info as $b) {
            // 讀取文件
            if (!$in = @fopen($dir.'/'.$b, "rb")) {
                break;
            }

            // 寫入文件
            while ($buff = fread($in, 4096)) {
                fwrite($out, $buff);
            }

            @fclose($in);
            @unlink($dir.'/'.$b);
        }
        flock($out, LOCK_UN);
    }
    @fclose($out);
    @rmdir($dir);

    echo json_encode(["code"=>"0"]);//隨便返回個值,實際中根據須要返回

看着挺長,實際就一個意思,按順序寫入。

五:其餘

特殊效果也加了一點,能夠試試

// 文件上傳過程當中建立進度條實時顯示。
    uploader.on('uploadProgress', function (file, percentage) {
        $("#percentage_a").css("width",parseInt(percentage * 100)+"%");
        $("#percentage").html(parseInt(percentage * 100) +"%");
    });

    // 上傳出錯處理
    uploader.on('uploadError', function (file) {
        uploader.retry();
    });

    // 暫停處理
    $("#stop").click(function(e){
        log("暫停上傳...");
        uploader.stop(true);
    })

    // 從暫停文件繼續
    $("#start").click(function(e){
        log("恢復上傳...");
        uploader.upload();
    })

 

五:PS

  1. 其實須要作的還有不少,各類驗證,必定要保證分片的正確上傳和寫入。
  2. 還有各類的錯誤處理,以後若是運用到項目中的話,也必定會回來補充須要注意的地方
  3. 做爲這個知識領域的小白,感受很奇妙,也頗有意思。

六:展現效果

  能夠對頁面進行下改動,也挺漂亮了,感謝。

相關文章
相關標籤/搜索