前言

最近在开发一个视频播放网站,用的是PHP技术。视频播放网站用户上传的都是视频资料,小的有几十M,大的都超过G了,对于这种超大文件的上传,我们不能用常规的上传方式,必须对大文件进行分块上传,否则一方面会造成服务器内存爆满,另一方如果上传的文件太大也很容造成上传超时的情况发生。

PHP上传超大文件代码

index.html

<div class="form-group">
    <label class="col-sm-2 control-label no-padding-right" for="form-field-1"> 上传视频 </label>
    <div class="col-sm-6">
        <div id="uploader" class="wu-example">
            <!--堆代码 duidaima.com -->
            <!--用来存放文件信息-->
            <div class="filename"></div>
            <div class="state"></div>
            <div class="progress">
                <div class="progress-bar progress-bar-info progress-bar-striped active" role="progressbar" aria-valuenow="40" aria-valuemin="0"
                    aria-valuemax="100" style="width: 0%">
                    <span class="sr-only">40% Complete (success)</span>
            </div>
        </div>
        <input type="hidden" name="video">
        <div class="btns">
            <div id="picker">选择文件</div>
            <button id="ctlBtn" class="btn btn-default">开始上传</button>
            <div id="pause" class="btn btn-danger">暂停上传</div>
        </div>
    </div>
</div>
</div>
<link rel="stylesheet" href="__PUBLIC__/webuploader/webuploader.css">
<script type="http://fex.baidu.com/webuploader/js/jquery-1.10.2.min.js"></script>
<script src="__PUBLIC__/webuploader/webuploader.min.js"></script>
<script>
     $(function(){
 
        var fileMd5; 
       //监听分块上传过程中的三个时间点 
       WebUploader.Uploader.register({ 
           "before-send-file":"beforeSendFile", 
           "before-send":"beforeSend", 
           "after-send-file":"afterSendFile", 
       },{ 
           //时间点1:所有分块进行上传之前调用此函数 
           beforeSendFile:function(file){ 
               var deferred = WebUploader.Deferred(); 
               //1、计算文件的唯一标记,用于断点续传 
               (new WebUploader.Uploader()).md5File(file,0,10*1024*1024) 
                   .progress(function(percentage){ 
                       // $('#item1').find("p.state").text("正在读取文件信息..."); 
                   }) 
                   .then(function(val){ 
                       fileMd5=val; 
                       // $('#item1').find("p.state").text("成功获取文件信息..."); 
                       //获取文件信息后进入下一步 
                       deferred.resolve(); 
                   }); 
               return deferred.promise(); 
           }, 
           //时间点2:如果有分块上传,则每个分块上传之前调用此函数 
           beforeSend:function(block){ 
               var deferred = WebUploader.Deferred(); 
               // 同步校验,防止没校验完就上传了
               $.ajaxSetup({async : false});
               $.ajax({ 
                   type:"POST", 
                   url:"{:url('Ajax/check_breakpoint')}", 
                   data:{ 
                       //文件唯一标记 
                       fileMd5:fileMd5, 
                       //当前分块下标 
                       chunk:block.chunk, 
                       //当前分块大小 
                       chunkSize:block.end-block.start,
                       chunks:block.chunks
                   }, 
                   dataType:"json", 
                   success:function(response){ 
                       if(response.ifExist==1){ 
                           //分块存在,跳过 
                           deferred.reject(); 
                       }else{ 
                           //分块不存在或不完整,重新发送该分块内容 
                           deferred.resolve(); 
                       } 
                   } 
               }); 
               $.ajaxSetup({async : true}); 
               this.owner.options.formData.fileMd5 = fileMd5; 
               deferred.resolve(); 
               return deferred.promise(); 
           }, 
           //时间点3:所有分块上传成功后调用此函数 
           afterSendFile:function(file){ 
               //如果分块上传成功,则通知后台合并分块 
                     $.post("{:url('Ajax/vupload_merge')}", { fileMd5: fileMd5, fileName: file.name }, function (data) {
                       if (data==0) {
                           $("#uploader .state").html("上传完成");
                       }
                });
           } 
       });   
      
       
         var _extensions = '3gp,mp4,rmvb,mov,avi,m4v';
         var _mimeTypes = 'video/*,audio/*,application/*';           
         var GUID = WebUploader.Base.guid();//一个GUID
         var uploader = WebUploader.create({
             swf: '__PUBLIC/webUploader/Uploader.swf',
             server: "{:url('Ajax/vupload')}",
             pick: '#picker',
             resize: false,
             chunked: true,//开始分片上传
             chunkSize: 5*1024*1024,//每一片的大小
             accept: {
                        title: '视频上传',
                        extensions: _extensions,
                        mimeTypes: _mimeTypes
                    },
            fileNumLimit: 1,//文件上传数量限制 
            threads: 1,
            formData: {
                 guid: GUID //自定义参数,待会儿解释
            }
         });
             
         uploader.on('uploadProgress', function (file, percentage) {
             $("#uploader .progress-bar").width(percentage * 100 + '%');
             $("#uploader .progress-bar").text(parseInt(percentage * 100) +"%");
         });
         uploader.on('uploadSuccess', function () {
             $("#uploader .progress-bar").removeClass('progress-bar-striped').removeClass('acti').removeClass('progress-bar-info').addClass('progress-bar-success');
             $("#uploader .state").html("上传成功...");
 
         });
         uploader.on('uploadError', function () {
             $("#uploader .progress-bar").removeClass('progress-bar-striped').removeClass('acti').removeClass('progress-bar-info').addClass('progress-bar-danger');
             $("#uploader .state").html("上传失败...");
         });
 
         $("#ctlBtn").click(function () {
             uploader.upload();
             $("#ctlBtn").text("上传");
             $('#ctlBtn').attr('disabled', 'disabled');
             $("#uploader .progress-bar").addClass('progress-bar-striped').addClass('active');
             $("#uploader .state").html("上传中...");
         });
         $('#pause').click(function () {
             uploader.stop(true);
             $('#ctlBtn').removeAttr('disabled');
             $("#ctlBtn").text("继续上传");
             $("#uploader .state").html("暂停中...");
             $("#uploader .progress-bar").removeClass('progress-bar-striped').removeClass('acti');
         });
    })
</script>

Ajax.php

<?php
//
+----------------------------------------------------------------------
// | thinkpphp [ WE CAN DO IT MORE SIMPLE ]
//
+----------------------------------------------------------------------
// | Copyright (c) 2018 rights reserved.
// +----------------------------------------------------------------------
// | Author: luyunoob 
//
+----------------------------------------------------------------------
namespace app\admin\controller;
 
use think\Db;
use think\Cache;
use think\Session;
class Ajax
{
 
    //检测是否有断点
    public
function check_breakpoint(){
       
$post=request()->post();
        // 找出分片目录
       
$dir=ROOT_PATH . 'data' . DS . 'upload'.DS.'video'.DS.$post['fileMd5'];
        if
(file_exists($dir)) {
            // 扫描文件
            $block_info=scandir($dir);
            // 去除无用文件
           
foreach ($block_info as $key => $block) {
                
if ($block == '.' || $block == '..') unset($block_info[$key]);
            }
           
natsort($block_info);
           
$end=end($block_info);
            if
($end>$post['chunk'] || $end==$post['chunk']) {
               
echo
json_encode(["ifExist"=>"1",'block_info'=>$end]);
            }
             
        }else{
            // 无断点
            echo
json_encode(["ifExist"=>"0"]);
        }
 
    }
 
    // 视频上传
    public
function vupload(){
       
$post=request()->post();
       
$dir=ROOT_PATH . 'data' . DS . 'upload'.DS.'video'.DS.$post['fileMd5'];
        $file =
request()->file('file');
        if
(file_exists($dir)) {
            $block_info=scandir($dir);
           
natsort($block_info);
            // 去除无用文件
           
foreach ($block_info as $key => $block) {
                
if ($block == '.' || $block == '..') unset($block_info[$key]);
            }
            $end=end($block_info);
            if
($post['chunk']>$end) {
               
$info = $file->move($dir.DS.$post['chunk'],'');
 
            }
 
            if
(isset($info)) {
               
die('{"status":1,"msg":"正在上传请稍等"}');
           
}else{
                die('{"status":0,"msg":"跳过 "}');
            }
        }else{
            if
(empty($post['chunk'])) {
               
$info = $file->move($dir.DS.'0','');
           
}else{
               
$info = $file->move($dir.DS.$post['chunk'],'');
            }
            if
($info) {
               
die('{"status":1,"msg":"正在上传请稍等"}');
            }
        }
         
         
    }
 
    // 合并视频
    public
function vupload_merge()
    {
       
$post=request()->post();
       
$dir=ROOT_PATH . 'data' . DS . 'upload'.DS.'video'.DS.$post['fileMd5'];
       
$block_info = scandir($dir);
         // 除去无用文件
         foreach
($block_info as $key => $block) {
             if
($block == '.' || $block == '..') unset($block_info[$key]);
         }
         // 数组按照正常规则排序
        
natsort($block_info);
         // 定义保存文件
        
$save_file = ROOT_PATH . 'data' . DS .
'upload'.DS.'video'.DS.date('Ymd');
 
         // 没有?建立
         if
(!file_exists($save_file)) {
           
@mkdir ($save_file,0755,true);
         };
         $count=count($block_info);
            // 开始写入
            // 获取文件后缀
           
$name=explode('.',$post['fileName']);
           
$ext=end($name);
            $out
= @fopen($save_file.DS.date('Ymdhis').'.'.$ext, "wb");
           
$url='video'.DS.date('Ymd').DS.date('Ymdhis').'.'.$ext;
            // 增加文件锁
            if
(flock($out, LOCK_EX)) {
               
foreach ($block_info as $b) {
                   
// 读取文件
               
if (!$in = @fopen($dir.DS.$b.DS.$post['fileName'], "rb")) {
                    break;
                }
                   
// 写入文件
                   
while ($buff = fread($in, 4096)) {
                       
fwrite($out, $buff);
                   
}
 
                   
@fclose($in);
                   
@unlink($dir.'/'.$b);
                }
               
flock($out, LOCK_UN);
            }
           
@fclose($out);
           
delete_dir_file($dir);
         echo
json_encode(["code"=>"0",'url'=>$url]);//   }
   
}

common.php

<?php
/**
 * 循环删除目录和文件
 * @param string
$dir_name
 * @return bool
 */
function delete_dir_file($dir_name)
{
    $result =
false;
    if
(is_dir($dir_name)) {
        if
($handle = opendir($dir_name)) {
            while
(false !== ($item = readdir($handle))) {
               
if ($item != '.' && $item != '..') {
                   
if (is_dir($dir_name . DS . $item)) {
                       
delete_dir_file($dir_name . DS . $item);
                   
} else {
                       
unlink($dir_name . DS . $item);
                   
}
                }
            }
           
closedir($handle);
            if
(rmdir($dir_name)) {
                $result = true;
            }
        }
    }
 
    return
$result;
}
 
?>

总结:

以上就是PHP上传超大文件的实现方式,对于这种超大文件的上传主要思路就是化整为零,把大的文件拆分成小的文件进行上传就可以了,这里面主要还涉及到断点续传等问题,好了,如果你也有遇到上传超大文件超时等问题,可以参考一下这篇文章的实现思路(完)。

 

 

参考文章:http://blog.ncmem.com/wordpress/2023/09/20/php如何上传超大文件/


阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: php