zoukankan      html  css  js  c++  java
  • 使用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,先整体跑下来在说。

    下面这段代码做了这些事

    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. 作为这个知识领域的小白,感觉很奇妙,也很有意思。

    六:展示效果

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

  • 相关阅读:
    android闹钟小案例之知识点总结
    转载---程序员发展之路
    android闹钟小案例之功能阐述
    基础篇:2.熟练掌握相关监听器的注册和使用
    基础篇:1.掌握基本组件和容器组件的使用
    微博分享、注销功能的实现
    新浪微博授权认证的实现
    新浪微博分享功能的简单实现
    读《编程高手箴言》笔记一
    Spark内核架构核心组件.txt
  • 原文地址:https://www.cnblogs.com/25-lH/p/8398781.html
Copyright © 2011-2022 走看看