PHP大文件分割上传 PHP分片上传


Posted in PHP onAugust 28, 2017

服务端为什么不能直接传大文件?跟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 相关文章推荐
第十一节 重载 [11]
Oct 09 PHP
一个php作的文本留言本的例子(六)
Oct 09 PHP
屏蔽浏览器缓存另类方法
Oct 09 PHP
回答PHPCHINA上的几个问题:URL映射
Feb 14 PHP
实用函数7
Nov 08 PHP
深入PHP curl参数的详解
Jun 17 PHP
解析php session_set_save_handler 函数的用法(mysql)
Jun 29 PHP
php判断字符串在另一个字符串位置的方法
Feb 27 PHP
ThinkPHP2.0读取MSSQL提示Incorrect syntax near the keyword 'AS'的解决方法
Jun 25 PHP
Laravel学习教程之IOC容器的介绍与用例
Aug 15 PHP
thinkphp5框架API token身份验证功能示例
May 21 PHP
设定php简写功能的方法
Nov 28 PHP
thinkphp ajaxfileupload实现异步上传图片的示例
Aug 28 #PHP
PHP实现超简单的SSL加密解密、验证及签名的方法示例
Aug 28 #PHP
PHP实现的简单对称加密与解密方法实例小结
Aug 28 #PHP
php检查函数必传参数是否存在的实例详解
Aug 28 #PHP
浅谈Laravel队列实现原理解决问题记录
Aug 19 #PHP
yii2 commands模式以及配置crontab定时任务的方法
Aug 19 #PHP
利用 fsockopen() 函数开放端口扫描器的实例
Aug 19 #PHP
You might like
模仿OSO的论坛(三)
2006/10/09 PHP
在php和MySql中计算时间差的方法
2011/04/22 PHP
精美漂亮的php分页类代码
2013/04/02 PHP
php json_encode值中大括号与花括号区别
2013/09/30 PHP
PHP邮件群发机实现代码
2016/02/16 PHP
zend框架实现支持sql server的操作方法
2016/12/08 PHP
Javascript匿名函数的一种应用 代码封装
2010/06/27 Javascript
获取客户端网卡MAC地址和IP地址实现JS代码
2013/03/17 Javascript
事件冒泡是什么如何用jquery阻止事件冒泡
2013/03/20 Javascript
iframe的父子窗口之间的对象相互调用基本用法
2013/09/03 Javascript
express的中间件basicAuth详解
2014/12/04 Javascript
jQuery性能优化技巧分析
2015/02/20 Javascript
jQuery Validate初步体验(一)
2015/12/12 Javascript
CKEditor无法验证的解决方案(js验证+jQuery Validate验证)
2016/05/09 Javascript
Js类的静态方法与实例方法区分及jQuery拓展的两种方法
2016/06/03 Javascript
AngularJs  E2E Testing 详解
2016/09/02 Javascript
node 利用进程通信实现Cluster共享内存
2017/10/27 Javascript
Node.js学习教程之HTTP/2服务器推送【译】
2017/10/31 Javascript
通过Nodejs搭建网站简单实现注册登录流程
2019/06/14 NodeJs
Weex开发之地图篇的具体使用
2019/10/16 Javascript
javascript二维数组和对象的深拷贝与浅拷贝实例分析
2019/10/26 Javascript
在Vue中使用this.$store或者是$route一直报错的解决
2019/11/08 Javascript
iSlider手机端图片滑动切换插件使用详解
2019/12/24 Javascript
node.js中 mysql 增删改查操作及async,await处理实例分析
2020/02/11 Javascript
Python中使用PyHook监听鼠标和键盘事件实例
2014/07/18 Python
使用Python发送邮件附件以定时备份MySQL的教程
2015/04/25 Python
使用Python绘制图表大全总结
2017/02/11 Python
django celery redis使用具体实践
2019/04/08 Python
Python3 合并二叉树的实现
2019/09/30 Python
《北大荒的秋天》教学反思
2014/04/14 职场文书
小学生评语集锦
2014/04/18 职场文书
我心目中的好老师活动方案
2014/08/19 职场文书
人身意外保险授权委托书
2014/10/01 职场文书
违规违纪检讨书范文
2015/05/06 职场文书
2016年暑假学生家长评语
2015/12/01 职场文书
企业管理制度设计时要注意的几种“常见病”!
2019/04/19 职场文书