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

 

为什么要分片上传?

php.ini里面的几个配置:

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

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

memory_limit = 128M //内存上限

max_execution_time = 30 //最大执行时间

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

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

 

步骤:

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












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

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

2.html

上传按钮及进度条部分


选择文件

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

 

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

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( '
' + '

' + file.name + '

' + '

等待上传...

' + '
' ); }); // 文件上传过程中创建进度条实时显示。 uploader.on( 'uploadProgress', function( file, percentage ) { var $li = $( '#'+file.id ), $percent = $li.find('.progress .progress-bar'); // 避免重复创建 if ( !$percent.length ) { $percent = $('
' + '
' + '
' + '
').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.上传中

使用webuploader实现大文件分片上传_第1张图片

2.上传完成

使用webuploader实现大文件分片上传_第2张图片

使用webuploader实现大文件分片上传_第3张图片

 

注:

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

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

2.视频播放失败

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

 

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

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

2018.10.11

你可能感兴趣的:(PHP)