使用webuploader实现大文件分片上传

 

为什么要分片上传?

php.ini里面的几个配置:

upload_max_filesize = 2M //PHP最大能接受的文件大小

post_max_size = 8M //PHP能收到的最大POST值'

memory_limit = 128M //内存上限

max_execution_time = 30 //最大执行时间

看出,如果想上传几百兆甚至几个G的文件势必会造成服务器内存资源吃光的问题,

所以我们使用webuploader,实现分片与并发结合,将一个大文件分割成多块,并发上传,也能极大提高大文件的上传速度。

 

步骤:

1.页面中引入必要资源文件

<!--视频播放插件video.js-->
<link href="video-js/css/video-js.css" rel="stylesheet">
<script src="video-js/js/videojs-ie8.min.js"></script>
<script src="video-js/js/video.js"></script>

<!--引入webuploader CSS-->
<link rel="stylesheet" type="text/css" href="webuploader/webuploader.css">
<link rel="stylesheet" type="text/css" href="webuploader/bootstrap-theme.min.css">
<!--引入webuploader JS-->
<script type="text/javascript" src="webuploader/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="webuploader/webuploader.js"></script>
<script type="text/javascript" src="webuploader/getting-started.js"></script>

webuploader需要的资源文件下载地址:http://fex.baidu.com/webuploader/download.html (官方站点还包括文档、API、Demo等)

video.js下载地址:https://github.com/videojs/video.js 下载后取src文件夹下的css、js即可 

2.html

上传按钮及进度条部分

<label style="font-weight: bold;padding: 0 0 5px 0">上传文件</label>
<div id="uploader" class="wu-example">
    <div id="thelist" class="uploader-list"></div>
    <div class="btns">
        <div id="picker" class="webuploader-container">
             <div class="webuploader-pick">选择文件</div>
             <div id="rt_rt_1bislfn1213jd12ig15s8e1ha2h1" style="position: absolute; top: 0px; left: 0px; width: 88px; height: 34px; overflow: hidden; bottom: auto; right: auto;">
                 <input type="file" name="videoUrl" class="webuploader-element-invisible" multiple="multiple">
                 <label style="opacity: 0; width: 100%; height: 100%; display: block; cursor: pointer; background: rgb(255, 255, 255);"></label>
             </div>
         </div>
         <button id="ctlBtn" class="btn btn-default" >开始上传</button>
     </div>
</div>

视频展示部分(默认不显示),上传成功后jquery修改为display:block

 <div id="videobox" style="display: none">
     <video id="example_video_1" class="video-js vjs-default-skin" controls preload="none" poster="" data-setup="{}" width="500px" height="300px">
        <source src='' type="video/mp4" id="sourse">
     </video>
</div>

form表单,上传成功后jquery将文件地址赋给input[name='videourl']的value

<form method="post" action="{:U("Video/doInsert")}" id="mainForm" enctype="multipart/form-data">
    <p style="padding-top:20px;">
	    <label>视频名称</label>
		<input class="text-input large-input" type="text" name="title" id="title" />
	</p>							
    <p>
        <label>视频截图</label>
        <input class="medium-input" type="file" name="attach_file" id="attach_file" />
    </p>
    <p>
        <input type="hidden" value="{:U('Video/VideoUpload')}" id="action">
        <input type="hidden" value="./Public" id="froot">
        <input type="hidden" value="../../../Uploads/" id="Uroot">
        <input class="button" type="submit" name="submit" value="提交更新"/>
        <input class="button" type="reset" name="button" id="button" value="还原重填"/>
        <input name="videourl" type="hidden" id="videourl" value="" />
     </p>
</form>

3.关键代码

getting-started.js

// 文件上传
jQuery(function() {
    var $ = jQuery,
        $list = $('#thelist'),
        $btn = $('#ctlBtn'),
        state = 'pending',
        uploader;

    var server = $('input#action').val();
    var froot = $('input#froot').val();
    var Uroot = $('input#Uroot').val();
    uploader = WebUploader.create({

        // 不压缩image
        resize: false,

        // swf文件路径
        swf: froot + '/Admin/webuploader/Uploader.swf',

        // 文件接收服务端。
        server: server,

        chunked:true,
        // 分片大小
        chunkSize:20 *1024 * 1024,   //20M
        //最大上传的文件数量, 总文件大小,单个文件大小(单位字节);
        fileNumLimit:1,
        fileSizeLimit:1000 * 1024 * 1024, //1G
        fileSingleSizeLimit:1000 * 1024 * 1024,  //1G

        // 选择文件的按钮。可选。
        // 内部根据当前运行是创建,可能是input元素,也可能是flash.
        pick: '#picker',

        //只允许选择图片
        accept: {
           title: 'Video',
           extensions: 'mp4',
           mimeTypes: 'video/mp4'
        },
        duplicate :false //防止多次上传
    });

    // 当有文件添加进来的时候
    uploader.on( 'fileQueued', function( file ) {
        $list.append( '<div id="' + file.id + '" class="item">' +
            '<h4 class="info">' + file.name + '</h4>' +
            '<p class="state">等待上传...</p>' +
            '</div>' );
    });

    // 文件上传过程中创建进度条实时显示。
    uploader.on( 'uploadProgress', function( file, percentage ) {
        var $li = $( '#'+file.id ),
            $percent = $li.find('.progress .progress-bar');

        // 避免重复创建
        if ( !$percent.length ) {
            $percent = $('<div class="progress progress-striped active">' +
                '<div class="progress-bar" role="progressbar" style="width: 0%">' +
                '</div>' +
                '</div>').appendTo( $li ).find('.progress-bar');
        }

        $li.find('p.state').text('上传中');

        $percent.css( 'width', percentage * 100 + '%' );
        $percent.css( 'height',  '10px' );
        $percent.css( 'margin-bottom',  '20px' );
    });

    uploader.on( 'uploadSuccess', function( file , response) {
        var fileinfojson =response._raw;//上传图片路径
        var obj = new Function("return" + fileinfojson)();//转换后的JSON对象
        var fileurl = obj.oldName;
        $('input#videourl').val(fileurl);
        $('#videobox').css('display','block');
        $('video#example_video_1_html5_api').attr('src',Uroot + fileurl);
        $('#sourse').attr('src',Uroot+fileurl);

        $( '#'+file.id ).find('p.state').text('已上传');

        $('input.webuploader-element-invisible').click(function(){
            $( '#'+file.id ).find('p.state').text('');
            uploader.removeFile(file);
            for(var i=0;i<100;i++){
                $('#' + 'WU_FILE_' + i).css('display','none');
            }
            })
    });

    uploader.on( 'uploadError', function( file ) {
        $( '#'+file.id ).find('p.state').text('上传出错');
    });

    uploader.on( 'uploadComplete', function( file ) {
        $( '#'+file.id ).find('.progress').fadeOut();
    });

    uploader.on( 'all', function( type ) {
        if ( type === 'startUpload' ) {
            state = 'uploading';
        } else if ( type === 'stopUpload' ) {
            state = 'paused';
        } else if ( type === 'uploadFinished' ) {
            state = 'done';
        }

        if ( state === 'uploading' ) {
            $btn.text('等待上传...');
        } else {
            $btn.text('开始上传');
        }
    });

    $btn.on( 'click', function() {
        if ( state === 'uploading' ) {
            uploader.stop();
        } else {
            uploader.upload();
        }
    });
    // // 所有文件上传成功后调用
    // uploader.on('uploadFinished', function () {
    //     //清空队列
    //     uploader.reset();
    // });


});

VideoUpload.class.php(官网下载已提供,仅对路径做稍作修改即可)

 /**
     * 大文件切片上传
     */
    public function VideoUpload(){
        header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
        header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
        header("Cache-Control: no-store, no-cache, must-revalidate");
        header("Cache-Control: post-check=0, pre-check=0", false);
        header("Pragma: no-cache");
        if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
            exit; // finish preflight CORS requests here
        }
        if ( !empty($_REQUEST[ 'debug' ]) ) {
            $random = rand(0, intval($_REQUEST[ 'debug' ]) );
            if ( $random === 0 ) {
                header("HTTP/1.0 500 Internal Server Error");
                exit;
            }
        }

        $dot="/";
        $path=1;
        $setFolder = 'Video/';
        $setUserPath = makeFolderName($path) ;
        $uploadDir = UPLOAD_PATH.$dot.$setFolder.$setUserPath;
        $targetDir = $uploadDir.'/upload_tmp';
        $cleanupTargetDir = true; // Remove old files
        $maxFileAge = 5 * 3600; // Temp file age in seconds

        // header("HTTP/1.0 500 Internal Server Error");
        // exit;
        // 5 minutes execution time
        @set_time_limit(5 * 60);
        // Uncomment this one to fake upload time
        // usleep(5000);
        // Settings
        // $targetDir = ini_get("upload_tmp_dir") . DIRECTORY_SEPARATOR . "plupload";
        
        // 验证缓存目录是否存在不存在创建
        if (!file_exists($targetDir)) {
            @mkdir($targetDir);
        }
        // 验证缓存目录是否存在不存在创建
        if (!file_exists($uploadDir)) {
            @mkdir($uploadDir);
        }
        // Get 或 file 方式获取文件名
        if (isset($_REQUEST["name"])) {
            $fileName = $_REQUEST["name"];
        } elseif (!empty($_FILES)) {
            $fileName = $_FILES["file"]["name"];
        } else {
            $fileName = uniqid("file_");
        }
        $oldName = $fileName;//记录文件原始名字
        $filePath = $targetDir . $fileName;
        // $uploadPath = $uploadDir . DIRECTORY_SEPARATOR . $fileName;
        // Chunking might be enabled
        $chunk = isset($_REQUEST["chunk"]) ? intval($_REQUEST["chunk"]) : 0;
        $chunks = isset($_REQUEST["chunks"]) ? intval($_REQUEST["chunks"]) : 1;
        // 删除缓存校验
        if ($cleanupTargetDir) {
            if (!is_dir($targetDir) || !$dir = opendir($targetDir)) {
                die('{"jsonrpc" : "2.0", "error" : {"code": 100, "message": "Failed to open temp directory."}, "id" : "id"}');
            }
            while (($file = readdir($dir)) !== false) {
                $tmpfilePath = $targetDir  . $file;
                // If temp file is current file proceed to the next
                if ($tmpfilePath == "{$filePath}_{$chunk}.part" || $tmpfilePath == "{$filePath}_{$chunk}.parttmp") {
                    continue;
                }
                // Remove temp file if it is older than the max age and is not the current file
                if (preg_match('/\.(part|parttmp|mp4)$/', $file) && (@filemtime($tmpfilePath) < time() - $maxFileAge)) {
                    @unlink($tmpfilePath);
                }
            }
            closedir($dir);
        }
        // 打开并写入缓存文件
        if (!$out = @fopen("{$filePath}_{$chunk}.parttmp", "wb")) {
            die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}');
        }
        if (!empty($_FILES)) {
            if ($_FILES["file"]["error"] || !is_uploaded_file($_FILES["file"]["tmp_name"])) {
                die('{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "id" : "id"}');
            }
            // Read binary input stream and append it to temp file
            if (!$in = @fopen($_FILES["file"]["tmp_name"], "rb")) {
                die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
            }
        } else {
            if (!$in = @fopen("php://input", "rb")) {
                die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
            }
        }
        while ($buff = fread($in, 4096)) {
            fwrite($out, $buff);
        }
        @fclose($out);
        @fclose($in);
        rename("{$filePath}_{$chunk}.parttmp", "{$filePath}_{$chunk}.part");
        $index = 0;
        $done = true;
        for( $index = 0; $index < $chunks; $index++ ) {
            if ( !file_exists("{$filePath}_{$index}.part") ) {
                $done = false;
                break;
            }
        }
        //文件全部上传 执行合并文件
        if ( $done ) {
            $pathInfo = pathinfo($fileName);
            $hashStr = substr(md5($pathInfo['basename']),8,16);
            $hashName = time() . $hashStr . '.' .$pathInfo['extension'];
            $uploadPath = $uploadDir .$hashName;
            if (!$out = @fopen($uploadPath, "wb")) {
                die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}');
            }
            if ( flock($out, LOCK_EX) ) {
                for( $index = 0; $index < $chunks; $index++ ) {
                    if (!$in = @fopen("{$filePath}_{$index}.part", "rb")) {
                        break;
                    }
                    while ($buff = fread($in, 4096)) {
                        fwrite($out, $buff);
                    }
                    @fclose($in);
                    @unlink("{$filePath}_{$index}.part");
                }
                flock($out, LOCK_UN);
            }
            @fclose($out);
            /*/腾讯云--对象存储
            vendor('TencentYun/TencentYunSendFile');
            $Yun = new \TencentYunSendFile();
            $res = $Yun->uploadYun(array('tmp_name'=>$uploadPath,'name'=>$hashName),'video');
            var_dump($res);*/
            //引用第三方类库
            /*vendor('getid3.getid3');
            $getID3 = new \getID3(); //实例化类
            $ThisFileInfo = $getID3->analyze($uploadPath);//分析文件*/
            $response = array(
                'success'=>true,
                'oldName'=>$setFolder.$setUserPath.$hashName,
                //'filePaht'=>$hashName,
                //'filePaht'=>substr($uploadPath,1),
                'fileSuffixes'=>$pathInfo['extension'],
                'time' =>$ThisFileInfo['playtime_string'],
            );
            //删除源文件
            /*unlink($uploadPath);*/
            die(json_encode($response));
        }
        // Return Success JSON-RPC response
        die('{"jsonrpc" : "2.0", "result" : null, "id" : "id"}');
    }

 

效果

1.上传中

2.上传完成

 

注:

1.进度条显示加载完毕上传过程结束实际上上传失败

请检查上传的文件目录是否正确(是否存在,是否有权限),分片上传的过程中会把大文件按照定义的大小如500M文件,定义分片大小为128M,会分成4个分片文件分布上传,上传过程中可在目录中查看这些文件是否全部上传完毕。如代码正确,上传完毕后子文件会合并为大文件。

2.视频播放失败

路径路径路径,还是路径的问题,f12定位到source标签中查看src文件地址是否正确,变量是否解析等,不妨先手动写出正确地址查看视频能否正确播放再进行调试。

 

每日吃超过十块的盖饭都会考虑好久

却心系宇宙从哪来到哪去的普普通程序员

2018.10.11

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值