• 使用Webuploader大文件分片传输


    背景: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
    六:展示效果
    可以对页面进行下改动,也挺漂亮了,感谢
  • 相关阅读:
    Laravel入坑指南(5)——请求与响应
    Laravel入坑指南(4)——数据库(Mysql)
    CentOS7 开机网卡加载失败
    个人CKeditor的config.js配置
    取消ie浏览器edge浏览器输入框右边的叉和眼睛
    angularjs中ckeditor的destroy问题
    angular js ckeditor directive示例代码
    建立没有文件名的文件
    设置ckeditor文本框的宽度为百分比自适应
    js中遍历删除数组中的项(项目中遇到的问题解决)
  • 原文地址:https://www.cnblogs.com/xproer/p/10509710.html
Copyright © 2020-2023  润新知