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 水平的题目
May 30 PHP
AJAX的跨域访问-两种有效的解决方法介绍
Jun 22 PHP
如何在smarty中增加类似foreach的功能自动加载数据
Jun 26 PHP
PHP编程风格规范分享
Jan 15 PHP
destoon二次开发模板及调用语法汇总
Jun 21 PHP
php版本的cron定时任务执行器使用实例
Aug 19 PHP
PHP实现根据银行卡号判断银行
Apr 29 PHP
php类自动加载器实现方法
Jul 28 PHP
PHP文件操作之获取目录下文件与计算相对路径的方法
Jan 08 PHP
php opendir()列出目录下所有文件的实例代码
Oct 02 PHP
PHP+Ajax简单get验证操作示例
Mar 02 PHP
php7下的filesize函数
Sep 30 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简单命令代码集锦
2007/09/24 PHP
php中文繁体和简体相互转换的方法
2015/03/21 PHP
如何用javascript控制上传文件的大小
2006/10/26 Javascript
JavaScript 替换Html标签实现代码
2009/10/14 Javascript
Dom操作之兼容技巧分享
2011/09/20 Javascript
javascript使用中为什么10..toString()正常而10.toString()出错呢
2013/01/11 Javascript
JavaScript 对任意元素,自定义右键菜单的实现方法
2013/05/08 Javascript
js图片闪动特效可以控制间隔时间如几分钟闪动一下
2014/08/12 Javascript
js实现鼠标点击文本框自动选中内容的方法
2015/08/20 Javascript
javascript实现网页端解压并查看zip文件
2015/12/15 Javascript
jQuery实现本地预览上传图片功能
2016/01/08 Javascript
基于JavaScript实现购物网站商品放大镜效果
2016/09/06 Javascript
Vue.js开发环境搭建
2016/11/10 Javascript
概述BootStrap中role=&quot;form&quot;及role作用角色
2016/12/08 Javascript
jQuery实现立体式数字动态增加(animate方法)
2016/12/21 Javascript
Mobile Web开发基础之四--处理手机设备的横竖屏问题
2017/08/11 Javascript
jQuery实现的表格前端排序功能示例
2017/09/18 jQuery
详解vue2.0 不同屏幕适配及px与rem转换问题
2018/02/23 Javascript
iview Upload组件多个文件上传的示例代码
2018/09/30 Javascript
如何在vue里面优雅的解决跨域(路由冲突问题)
2019/01/20 Javascript
JavaScript面试技巧之数组的一些不low操作
2019/03/22 Javascript
基于vue-simple-uploader封装文件分片上传、秒传及断点续传的全局上传插件功能
2021/02/23 Vue.js
Python中的rjust()方法使用详解
2015/05/19 Python
解决nohup重定向python输出到文件不成功的问题
2018/05/11 Python
python组合无重复三位数的实例
2018/11/13 Python
美国在线肉类和海鲜配送:Crowd Cow
2020/10/02 全球购物
2019史上最全Database工程师题库
2015/12/06 面试题
校友会欢迎辞
2014/01/13 职场文书
优秀员工获奖感言
2014/03/01 职场文书
工厂搬迁方案
2014/05/11 职场文书
校园环保建议书
2014/05/14 职场文书
行政撤诉申请书
2015/05/18 职场文书
神秘岛读书笔记
2015/07/01 职场文书
2015年教师节感言
2015/08/03 职场文书
详解MySQL中的pid与socket
2021/06/15 MySQL
js 实现Material UI点击涟漪效果示例
2022/09/23 Javascript