使用Webuploader大文件分片传输

背景:50G大文件的HTTP上传至服务器。

好了,根据这个命题,可以开始研究我们怎么做才能把这么大的文件上传成功。

分片上传是肯定的,断点续传也是要有的,进度可视化那就更好了,基于这些,我选择了Webuploader在前端进行分片上传。

为什么选择它呢,好吧,它简单,易上手,好排错,文档多......

实际是我懒......

网上的教程大部分是复制粘贴,借鉴起来也很无奈,推荐一个我觉得比较实在的

https://0x9.me/q2aln

本篇Demo地址,欢迎各位大佬指点

https://0x9.me/q2aam

一:开始

新建立项目,这里用了php7.0版本后台处理。

没啥说的,下载WebUploader的包https://0x9.me/q2aln

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

六:展示效果

可以对页面进行下改动,也挺漂亮了,感谢。

猜你喜欢

转载自blog.csdn.net/germy2/article/details/87915474