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 飞信好友免费短信API接口开源版
Jul 22 PHP
Windows下利用Gvim写PHP产生中文乱码问题解决方法
Apr 20 PHP
ThinkPHP 连接Oracle数据库的详细教程[全]
Jul 16 PHP
PHP将session信息存储到数据库的类实例
Mar 04 PHP
php去掉文件前几行的方法
Jul 29 PHP
基于php实现的php代码加密解密类完整实例
Oct 12 PHP
DEDE实现转跳属性文档在模板上调用出转跳地址
Nov 04 PHP
CodeIgniter整合Smarty的方法详解
Aug 25 PHP
PHP7.1实现的AES与RSA加密操作示例
Jun 15 PHP
Laravel框架路由管理简单示例
May 07 PHP
Laravel中正确地返回HTTP状态码方法示例
Sep 10 PHP
Laravel框架集合用法实例浅析
May 14 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
使用 PHPMAILER 发送邮件实例应用
2012/11/07 PHP
php通过数组实现多条件查询实现方法(字符串分割)
2014/05/06 PHP
destoon切换城市后实现logo旁边显示地区名称的方法
2014/08/21 PHP
PHP输出缓冲控制Output Control系列函数详解
2015/07/02 PHP
Laravel如何同时连接多个数据库详解
2019/08/13 PHP
jQuery 可以拖动的div实现代码 脚本之家修正版
2009/06/26 Javascript
Bookmarklet实现启动jQuery(模仿 云输入法)
2010/09/15 Javascript
jQuery实现简单网页遮罩层/弹出层效果兼容IE6、IE7
2014/06/16 Javascript
jQuery实现分章节锚点“回到顶部”动画特效代码
2015/10/23 Javascript
基于HTML+CSS,jQuery编写的简易计算器后续(添加了键盘监听)
2016/01/05 Javascript
JavaScript操作选择对象的简单实例
2016/05/16 Javascript
jQuery+ajax简单实现文件上传的方法
2016/06/03 Javascript
javascript设计模式之Adapter模式【适配器模式】实现方法示例
2017/01/13 Javascript
基于JQuery及AJAX实现名人名言随机生成器
2017/02/10 Javascript
完美解决mui框架off-canvas侧滑超出部分隐藏无法滚动的问题
2018/01/25 Javascript
Vue表单及表单绑定方法
2018/09/04 Javascript
在layui中layer弹出层点击事件无效的解决方法
2019/09/05 Javascript
在vue中使用jsx语法的使用方法
2019/09/30 Javascript
解决vue使用vant轮播组件swipe + flex时文字抖动问题
2021/01/07 Vue.js
如何使用VSCode愉快的写Python于调试配置步骤
2018/04/06 Python
python使用docx模块读写docx文件的方法与docx模块常用方法详解
2020/02/17 Python
详解pandas中iloc, loc和ix的区别和联系
2020/03/09 Python
python Django 反向访问器的外键冲突解决
2020/05/20 Python
python属于解释型语言么
2020/06/15 Python
Python 实现劳拉游戏的实例代码(四连环、重力四子棋)
2021/03/03 Python
美国最大的半成品净菜电商:Blue Apron(蓝围裙)
2018/04/27 全球购物
介绍一下Linux中的链接
2016/05/28 面试题
关键字throw与throws的用法差异
2016/11/22 面试题
校三好学生主要事迹
2014/01/11 职场文书
如何写好优秀的创业计划书
2014/01/30 职场文书
安全生产汇报材料
2014/02/17 职场文书
2014迎国庆演讲稿
2014/09/19 职场文书
临时用工协议书范本
2014/10/29 职场文书
个人思想政治总结
2015/03/05 职场文书
企业爱心捐款倡议书
2015/04/27 职场文书
永中文档在线转换预览基于nginx配置部署方案
2022/06/10 Servers