背景:50G大文件的HTTP上傳至服務器。
好了,根據這個命題,能夠開始研究咱們怎麼作才能把這麼大的文件上傳成功。
分片上傳是確定的,斷點續傳也是要有的,進度可視化那就更好了,基於這些,我選擇了Webuploader在前端進行分片上傳。
爲何選擇它呢,好吧,它簡單,易上手,好排錯,文檔多......
實際是我懶......
網上的教程大部分是複製粘貼,借鑑起來也很無奈,推薦一個我以爲比較實在的
https://www.cnblogs.com/baiyunchen/p/5383507.html
本篇Demo地址,歡迎各位大佬指點
https://github.com/papapalh/Big_File
一:開始
新創建項目,這裏用了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,先總體跑下來在說。
下面這段代碼作了這些事
// 當有文件被添加進隊列的時候-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
六:展現效果
能夠對頁面進行下改動,也挺漂亮了,感謝