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 is_file()和is_dir()用于遍历目录时用法注意事项
Mar 02 PHP
php中判断字符串是否全是中文或含有中文的实现代码
Sep 16 PHP
PHP中如何定义和使用常量
Feb 28 PHP
php实现webservice实例
Nov 06 PHP
Yii不依赖Model的表单生成器用法实例
Dec 04 PHP
在PHP程序中使用Rust扩展的方法
Jul 03 PHP
php实现无限级分类(递归方法)
Aug 06 PHP
php仿微信红包分配算法的实现方法
May 13 PHP
用php+ajax新建流程(请假、进货、出货等)
Jun 11 PHP
PHPMailer使用QQ邮箱实现邮件发送功能
Aug 18 PHP
php和redis实现秒杀活动的流程
Jul 17 PHP
php使用yield对性能提升的测试实例分析
Sep 19 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中的global
2014/08/19 PHP
PHP5.4起内置web服务器使用方法
2016/08/09 PHP
PHP自定义函数获取汉字首字母的方法
2016/12/01 PHP
JQuery 遮罩层实现(mask)实现代码
2010/01/09 Javascript
js实现简单的星级选择器提交效果适用于评论等
2013/10/18 Javascript
jquery select 设置默认选中的示例代码
2014/02/07 Javascript
js 显示base64编码的二进制流网页图片
2014/04/04 Javascript
jsPDF导出pdf示例
2014/05/02 Javascript
老生常谈JavaScript数组的用法
2016/06/10 Javascript
Angular.js中angular-ui-router的简单实践
2017/07/18 Javascript
jquery实现倒计时小应用
2017/09/19 jQuery
node通过express搭建自己的服务器
2017/09/30 Javascript
jquery使用iscorll实现上拉、下拉加载刷新
2017/10/26 jQuery
js中this对象用法分析
2018/01/05 Javascript
Express之托管静态文件的方法
2018/06/01 Javascript
js实现时分秒倒计时
2019/12/03 Javascript
Vue替代marquee标签超出宽度文字横向滚动效果
2019/12/09 Javascript
JS常用排序方法实例代码解析
2020/03/03 Javascript
用VsCode编辑TypeScript的实现方法
2020/05/07 Javascript
Vue Render函数原理及代码实例解析
2020/07/30 Javascript
使用vue3重构拼图游戏的实现示例
2021/01/25 Vue.js
python3+PyQt5实现使用剪贴板做复制与粘帖示例
2017/01/24 Python
使用Python中的tkinter模块作图的方法
2017/02/07 Python
对pandas中iloc,loc取数据差别及按条件取值的方法详解
2018/11/06 Python
Python玩转加密的技巧【推荐】
2019/05/13 Python
在pycharm中配置Anaconda以及pip源配置详解
2019/09/09 Python
Python使用正则表达式实现爬虫数据抽取
2020/08/17 Python
CSS3弹性盒模型flex box快速入门心得(必看篇)
2016/05/24 HTML / CSS
HTML5 Web存储方式的localStorage和sessionStorage进行数据本地存储案例应用
2012/12/09 HTML / CSS
详解canvas绘制网络字体几种方法
2019/08/27 HTML / CSS
见习报告格式范文
2014/11/08 职场文书
医院保洁员管理制度
2015/08/05 职场文书
2016五一手机促销广告语
2016/01/28 职场文书
让文件路径提取变得更简单的Python Path库
2021/05/27 Python
简单聊一聊SQL注入及防止SQL注入
2022/03/23 MySQL
Ruby GDBM操作简介及数据存储原理
2022/04/19 Ruby