大文件上傳, 在web應(yīng)該用中是一個常規(guī)應(yīng)用, 然因為服務(wù)器配置設(shè)置最大上傳大小, 或者上傳時間太久沒有進度條等等, 都會給我們的使用帶來較大的困擾, 這里基于layui進度條+thinkphp6.0+webuploader的實現(xiàn), 分享給大家一個完整的案例
1, html代碼:
<script type="text/javascript" src="__STATIC__/js/jquery.js"></script> <!--引入 上傳大文件 用的插件 webuploade 的相關(guān)文件--> <link rel="stylesheet" type="text/css" href="__STATIC__/webuploader/webuploader.css"> <!--引入JS--> <script type="text/javascript" src="__STATIC__/webuploader/webuploader.js"></script> <div class="layui-form-item"> <label class="layui-form-label">上傳大文件</label> <div class="layui-input-block"> <div id="uploader" class="wu-example"> <!--用來存放文件信息--> <div id="thelist" class="uploader-list"></div> <div class="btns"> <div id="pickerfile" style="float:left;">選擇文件</div> <button id="startup" type="button" class="btn-default layui-btn" style="height: 44px; margin-left:10px; display: none;">開始上傳</button> </div> <table class="layui-table"> <thead> <tr> <th>文件名</th> <th>文件大小</th> <th>文件驗證</th> <th style="width: 300px;">進度</th> <th>操作</th> </tr> </thead> <tbody id="fileinfo"> </tbody> </table> <div id="infos" style="display:none;"> </div> </div> </div> </div>
2, JS代碼
引入layui
<script src="__LAYUIADMIN__/layui/layui.js"></script>
function formatFileSize(size){
var fileSize =0;
if(size/1024>1024){
var len = size/1024/1024;
fileSize = len.toFixed(2) +"MB";
}else if(size/1024/1024>1024){
var len = size/1024/1024;
fileSize = len.toFixeds(2)+"GB";
}else{
var len = size/1024;
fileSize = len.toFixed(2)+"KB";
}
return fileSize;
}
layui.config({
base: '__LAYUIADMIN__/' //靜態(tài)資源所在路徑
}).extend({
index: 'lib/index' //主入口模塊
}).use(['index', 'form', 'laydate','upload','element'], function(){
var $ = layui.$
,admin = layui.admin
,element = layui.element
,layer = layui.layer
,laydate = layui.laydate
,form = layui.form
,upload = layui.upload;
var uploader = WebUploader.create({
// swf文件路徑
swf: '__STATIC__/webuploader/Uploader.swf',
// 文件接收服務(wù)端。
server: '{:url("@qile/upload/getBlockFile")}',
// 選擇文件的按鈕??蛇x。
// 內(nèi)部根據(jù)當前運行是創(chuàng)建,可能是input元素,也可能是flash.
pick: {
"id":'#pickerfile',
"multiple":true //禁止多選。
},
chunked: true, //開啟分片上傳
chunkSize: 1 * 1024 * 1024, //每一片的大小
chunkRetry: 5, // 如果遇到網(wǎng)絡(luò)錯誤,重新上傳次數(shù)
threads: 3, //上傳并發(fā)數(shù)。允許同時最大上傳進程數(shù)。
// 不壓縮image, 默認如果是jpeg,文件上傳前會壓縮一把再上傳!
resize: false,
// 選完文件后,是否自動上傳。
auto: false,
// 只允許選擇圖片文件。
// accept: {
// title: 'Images',
// extensions: 'gif,jpg,jpeg,bmp,png',
// mimeTypes: 'image/*'
// }
});
// 當有文件被添加進隊列的時候
uploader.on('fileQueued', function( file ) {
$("#startup").hide(); //隱藏開始上傳按鈕 , 待全部檢驗完再顯示
let size = formatFileSize(file.size);
//根據(jù)文件大小定義id名字
let progress = "<div class='layui-progress' lay-showPercent=\"yes\">" +
"<div class='layui-progress-bar' lay-percent=''></div>" +
"</div>";
let html = "<tr id='"+ file.id +"'>" +
"<td>"+file.name+"</td>" +
"<td>"+size+"</td>" +
"<td class='md5'></td>" +
"<td class='state'>" + progress+ "</td>" +
"<td class='do'></td>" +
"</tr>";
$("#fileinfo").append(html);
element.render('progress');
uploader.md5File(file).progress(function (percentage) {
// 及時顯示進度
//console.log("測試進度");
let v = parseInt(percentage * 100);
$("#"+file.id).find(".md5").text(v+"%");
}).then(function (fileMd5) {
$("#"+file.id).find(".do").text('等待上傳...');
file.md5 = fileMd5;
var all_f = uploader.getFiles(); //隊列中的所有的文件
var nums = uploader.getFiles().length;
var count = 0;
for(var i = 0; i < nums; i++){
if(all_f[i].hasOwnProperty('md5')) count++;
}
if(nums == count){
//當隊列中的每一個文件都有了 md5 屬性時, 開啟文件上傳按鈕
$("#startup").show();
}
//計算大的文件是需要一段時間的
});
//file : 代表隊列中的各個文件
});
// 每個分塊發(fā)送前檢查,并附加MD5數(shù)據(jù)
uploader.on('uploadBeforeSend', function(block, data ) {
data.md5 = block.file.md5;
//data.status = block.file.status;
});
// 上傳提交
$("#startup").on('click', function() {
uploader.upload();
});
// 文件上傳過程中創(chuàng)建進度條實時顯示。
uploader.on( 'uploadProgress', function( file, percentage ) {
var id = "#" + file.id;
var value = parseInt(percentage * 100)+"%";
$(id).find(".layui-progress-bar").attr({"lay-percent":value});
$(id).find('.do').text('上傳中');
element.render('progress');
});
/*uploader.on( 'uploadSuccess', function( file ) {
$( '#'+file.id ).find('.do').text('已上傳');
});
uploader.on( 'uploadError', function( file ) {
$( '#'+file.id ).find('.do').text('上傳出錯');
});*/
// 上傳完成后觸發(fā)
uploader.on('uploadSuccess', function (file,response) {
$.post('{:url("@qile/upload/merge")}', { md5: file.md5, fileName: file.name }, function (obj) {
if (obj.status) {
//將上傳的文件用 input做記錄, 方便提交到數(shù)據(jù)庫中
var data = {md5: file.md5, source_name: file.name, size:file.size,save_name:obj.save_name};
var str_data = JSON.stringify(data);
var html = "<input type='text' class='info' name=\"info[]\" _id='"+file.id+"' value='"+str_data+"'>";
$("#infos").append(html);
//更新設(shè)置 狀態(tài)
var btn = '<button type="button" class="layui-btn layui-btn-sm layui-btn-danger big_del" _id="'+file.id+'" _save_name="'+obj.save_name+'">刪除</button>';
$("#"+file.id).find(".do").html(btn);
}
});
});
//上傳完成
//大文件上傳結(jié)束
//刪除
$(document).on("click",".big_del",function(){
let _this = $(this);
layer.confirm('確定要刪除嗎?', {icon: 3, title:'提示', title:""}, function(index_alert){
var _id = _this.attr("_id");
var data = {files:_this.attr("_save_name")}
$.post('{:url("@qile/upload/del")}',data);
_this.parent().parent("tr").remove(); //刪除本行
$("input[_id='"+_id+"']").remove(); //刪除
layer.close(index_alert);
})
})
//刪除結(jié)束
});3, 控制器接收文件
class UploadController
{
//接收大文件分開塊狀文件
public function getBlockFile(){
set_time_limit(0);
// 建立臨時目錄存放文件-以MD5為唯一標識
$post = request()->post();
$dir = str_replace("\\","/",public_path()) . "static/upload/" . $post["md5"];
if (!file_exists($dir)) {
mkdirs($dir,0777);
}
// 移動每一塊文件到 唯一 標識文件夾下
if($post["size"] >= 1 * 1024 * 1024){
move_uploaded_file($_FILES["file"]["tmp_name"], $dir.'/'.$post["chunk"]);
}
else{
//文件 小于 設(shè)置的 chunk 大小 時,沒有chunk
$file = $_FILES["file"];
move_uploaded_file($_FILES["file"]["tmp_name"], $dir.'/'.$file["name"]);
}
}
//合成分塊上傳的文件
public function merge(){
set_time_limit(0);
// 接收相關(guān)數(shù)據(jù)
$post = $_POST;
$path = str_replace("\\","/",public_path()) . "static/upload/";
// 找出分片文件
$dir = $path . $post["md5"];
// 獲取分片文件內(nèi)容
$block_info = scandir($dir);
// 除去無用文件
foreach ($block_info as $key => $block) {
if ($block == '.' || $block == '..') unset($block_info[$key]);
}
// 數(shù)組按照正常規(guī)則排序
natsort($block_info);
// 定義保存文件
$save_path = date("Ymd").'/';
$target_path = $path . $save_path;
if(!is_dir($target_path)) mkdirs($target_path);
$new_file_name = date('Ymdgis').rand().".".getSuffix($post['fileName']);
$save_file = $target_path . $new_file_name;
$save_name = $save_path . $new_file_name;
// 沒有?建立
if (!file_exists($save_file)) fopen($save_file, "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);
return json(["save_name" => $save_name, "status" => 1]);
}
//刪除提交的文件
public function del(){
$files = request()->post("files");
if($files){
$path = str_replace("\\","/",public_path()) . "static/upload/";
if(file_exists($path.$files)){
unlink($path.$files);
}
}
}
}4. 實例效果圖

