PHP大文件分割分片上传实现代码


Posted in PHP onDecember 09, 2020

服务端为什么不能直接传大文件?跟php.ini里面的几个配置有关

upload_max_filesize = 2M //PHP最大能接受的文件大小
post_max_size = 8M //PHP能收到的最大POST值'
memory_limit = 128M //内存上限
max_execution_time = 30 //最大执行时间

当然不能简单粗暴的把上面几个值调大,否则服务器内存资源吃光是迟早的问题。

解决思路

好在HTML5开放了新的FILE API,也可以直接操作二进制对象,我们可以直接在浏览器端实现文件切割,按照以前的做法就得用Flash的方案,实现起来会麻烦很多。

JS思路

1.监听上传按钮的onchange事件

2.获取文件的FILE对象

3.把文件的FILE对象进行切割,并且附加到FORMDATA对象中

4.把FORMDATA对象通过AJAX发送到服务器

5.重复3、4步骤,直到文件发送完。

PHP思路

1.建立上传文件夹

2.把文件从上传临时目录移动到上传文件夹

3.所有的文件块上传完成后,进行文件合成

4.删除文件夹

5.返回上传后的文件路径

DEMO代码

前端部分代码

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
     content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    #progress{
      width: 300px;
      height: 20px;
      background-color:#f7f7f7;
      box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);
      border-radius:4px;
      background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);
    }

    #finish{
      background-color: #149bdf;
      background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);
      background-size:40px 40px;
      height: 100%;
    }
    form{
      margin-top: 50px;
    }
  </style>
</head>
<body>
<div id="progress">
  <div id="finish" style="width: 0%;" progress="0"></div>
</div>
<form action="./upload.php">
  <input type="file" name="file" id="file">
  <input type="button" value="停止" id="stop">
</form>
<script>
  var fileForm = document.getElementById("file");
  var stopBtn = document.getElementById('stop');
  var upload = new Upload();

  fileForm.onchange = function(){
    upload.addFileAndSend(this);
  }

  stopBtn.onclick = function(){
    this.value = "停止中";
    upload.stop();
    this.value = "已停止";
  }

  function Upload(){
    var xhr = new XMLHttpRequest();
    var form_data = new FormData();
    const LENGTH = 1024 * 1024;
    var start = 0;
    var end = start + LENGTH;
    var blob;
    var blob_num = 1;
    var is_stop = 0
    //对外方法,传入文件对象
    this.addFileAndSend = function(that){
      var file = that.files[0];
      blob = cutFile(file);
      sendFile(blob,file);
      blob_num += 1;
    }
    //停止文件上传
    this.stop = function(){
      xhr.abort();
      is_stop = 1;
    }
    //切割文件
    function cutFile(file){
      var file_blob = file.slice(start,end);
      start = end;
      end = start + LENGTH;
      return file_blob;
    };
    //发送文件
    function sendFile(blob,file){
      var total_blob_num = Math.ceil(file.size / LENGTH);
      form_data.append('file',blob);
      form_data.append('blob_num',blob_num);
      form_data.append('total_blob_num',total_blob_num);
      form_data.append('file_name',file.name);

      xhr.open('POST','./upload.php',false);
      xhr.onreadystatechange = function () {
        var progress;
        var progressObj = document.getElementById('finish');
        if(total_blob_num == 1){
          progress = '100%';
        }else{
          progress = Math.min(100,(blob_num/total_blob_num)* 100 ) +'%';
        }
        progressObj.style.width = progress;
        var t = setTimeout(function(){
          if(start < file.size && is_stop === 0){
            blob = cutFile(file);
            sendFile(blob,file);
            blob_num += 1;
          }else{
            setTimeout(t);
          }
        },1000);
      }
      xhr.send(form_data);
    }
  }

</script>
</body>
</html>

PHP部分代码

<?php
class Upload{
  private $filepath = './upload'; //上传目录
  private $tmpPath; //PHP文件临时目录
  private $blobNum; //第几个文件块
  private $totalBlobNum; //文件块总数
  private $fileName; //文件名

  public function __construct($tmpPath,$blobNum,$totalBlobNum,$fileName){
    $this->tmpPath = $tmpPath;
    $this->blobNum = $blobNum;
    $this->totalBlobNum = $totalBlobNum;
    $this->fileName = $fileName;
    
    $this->moveFile();
    $this->fileMerge();
  }
  
  //判断是否是最后一块,如果是则进行文件合成并且删除文件块
  private function fileMerge(){
    if($this->blobNum == $this->totalBlobNum){
      $blob = '';
      for($i=1; $i<= $this->totalBlobNum; $i++){
        $blob .= file_get_contents($this->filepath.'/'. $this->fileName.'__'.$i);
      }
      file_put_contents($this->filepath.'/'. $this->fileName,$blob);
      $this->deleteFileBlob();
    }
  }
  
  //删除文件块
  private function deleteFileBlob(){
    for($i=1; $i<= $this->totalBlobNum; $i++){
      @unlink($this->filepath.'/'. $this->fileName.'__'.$i);
    }
  }
  
  //移动文件
  private function moveFile(){
    $this->touchDir();
    $filename = $this->filepath.'/'. $this->fileName.'__'.$this->blobNum;
    move_uploaded_file($this->tmpPath,$filename);
  }
  
  //API返回数据
  public function apiReturn(){
    if($this->blobNum == $this->totalBlobNum){
        if(file_exists($this->filepath.'/'. $this->fileName)){
          $data['code'] = 2;
          $data['msg'] = 'success';
          $data['file_path'] = 'http://'.$_SERVER['HTTP_HOST'].dirname($_SERVER['DOCUMENT_URI']).str_replace('.','',$this->filepath).'/'. $this->fileName;
        }
    }else{
        if(file_exists($this->filepath.'/'. $this->fileName.'__'.$this->blobNum)){
          $data['code'] = 1;
          $data['msg'] = 'waiting for all';
          $data['file_path'] = '';
        }
    }
    header('Content-type: application/json');
    echo json_encode($data);
  }
  
  //建立上传文件夹
  private function touchDir(){
    if(!file_exists($this->filepath)){
      return mkdir($this->filepath);
    }
  }
}

//实例化并获取系统变量传参
$upload = new Upload($_FILES['file']['tmp_name'],$_POST['blob_num'],$_POST['total_blob_num'],$_POST['file_name']);
//调用方法,返回结果
$upload->apiReturn();

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
Php做的端口嗅探器--可以指定网站和端口
Oct 09 PHP
[原创]PHP中通过ADODB库实现调用Access数据库之修正版本
Dec 31 PHP
php smarty模版引擎中的缓存应用
Dec 11 PHP
ajax返回值中有回车换行、空格的解决方法分享
Oct 24 PHP
php输入流php://input使用浅析
Sep 02 PHP
php字符串按照单词进行反转的方法
Mar 14 PHP
PHP文件操作方法汇总
Jul 01 PHP
PHP抽奖算法程序代码分享
Oct 08 PHP
PHP图形操作之Jpgraph学习笔记
Dec 25 PHP
thinkphp中字符截取函数msubstr()用法分析
Jan 09 PHP
laravel 操作数据库常用函数的返回值方法
Oct 11 PHP
基于Laravel 多个中间件的执行顺序详解
Oct 21 PHP
PHP rsa加密解密算法原理解析
Dec 09 #PHP
PHP如何解决微信文章图片防盗链
Dec 09 #PHP
PHP sdk文档处理常用代码示例解析
Dec 09 #PHP
PHP sdk实现在线打包代码示例
Dec 09 #PHP
基于PHP实现生成随机水印图片
Dec 09 #PHP
PHP实现腾讯短网址生成api接口实例
Dec 08 #PHP
win10下 php安装seaslog扩展的详细步骤
Dec 04 #PHP
You might like
PHP中数据类型转换的三种方式
2015/04/02 PHP
轻轻松松学习JavaScript
2007/02/25 Javascript
JavaScript的9个陷阱及评点分析
2008/05/16 Javascript
javascript 可以拖动的DIV(二)
2009/06/26 Javascript
js自动生成对象的属性示例代码
2013/10/28 Javascript
jquery提交form表单简单示例分享
2014/03/03 Javascript
jquery实现显示已选用户
2014/07/21 Javascript
jQuery应用之jQuery链用法实例
2015/01/19 Javascript
浅析ES6的八进制与二进制整数字面量
2016/08/30 Javascript
javascript cookie用法基础教程(概念,设置,读取及删除)
2016/09/20 Javascript
jQuery中animate的几种用法与注意事项
2016/12/12 Javascript
js实现做通讯录的索引滑动显示效果和滑动显示锚点效果
2017/02/18 Javascript
js实现拖拽功能
2017/03/01 Javascript
详解JS浏览器事件循环机制
2019/03/27 Javascript
ajax跨域访问遇到的问题及解决方案
2019/05/23 Javascript
vue 组件开发原理与实现方法详解
2019/11/29 Javascript
JS实现简单日历特效
2020/01/03 Javascript
详解Python的Django框架中的Cookie相关处理
2015/07/22 Python
使用Python判断质数(素数)的简单方法讲解
2016/05/05 Python
Anaconda入门使用总结
2018/04/05 Python
python中退出多层循环的方法
2018/11/27 Python
django实现模板中的字符串文字和自动转义
2020/03/31 Python
Django Admin后台模型列表页面如何添加自定义操作按钮
2020/11/11 Python
python 实现表情识别
2020/11/21 Python
css3的transform造成z-index无效解决方案
2014/12/04 HTML / CSS
浅谈HTML5新增及移除的元素
2016/06/27 HTML / CSS
Ted Baker英国官网:男士和女士服装及配件
2017/03/13 全球购物
美国的Eastbay旗下的运动款子品牌:Final-Score
2018/01/01 全球购物
Ajax的优点和缺点
2014/11/21 面试题
打架检讨书500字
2014/01/29 职场文书
运动会跳远加油稿
2014/02/20 职场文书
家长学校工作方案
2014/05/07 职场文书
模具设计与制造专业求职信
2014/07/19 职场文书
党的群众路线教育实践活动个人对照检查剖析材料
2014/09/23 职场文书
人事局接收函
2015/01/30 职场文书
考勤制度通知
2015/04/25 职场文书