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

    六:展示效果

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

  • 相关阅读:
    Single Number II
    Pascal's Triangle
    Remove Duplicates from Sorted Array
    Populating Next Right Pointers in Each Node
    Minimum Depth of Binary Tree
    Unique Paths
    Sort Colors
    Swap Nodes in Pairs
    Merge Two Sorted Lists
    Climbing Stairs
  • 原文地址:https://www.cnblogs.com/25-lH/p/8398781.html
Copyright © 2011-2022 走看看