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 相关文章推荐
PHP5.0正式发布 不完全兼容PHP4 新增多项功能
Oct 09 PHP
我常用的几个类
Oct 09 PHP
joomla内置的表单验证功能使用方法
Jun 11 PHP
curl实现站外采集的方法和技巧
Jan 31 PHP
用PHP来计算某个目录大小的方法
Apr 01 PHP
php数组合并array_merge()函数使用注意事项
Jun 19 PHP
php格式化时间戳显示友好的时间实现思路及代码
Oct 23 PHP
ThinkPHP模版中导入CSS和JS文件的方法
Nov 29 PHP
php页面缓存方法小结
Jan 10 PHP
PHP获取ttf格式文件字体名的方法示例
Mar 06 PHP
Thinkphp5.0 框架使用模型Model添加、更新、删除数据操作详解
Oct 11 PHP
Yii框架应用组件用法实例分析
May 15 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
在PHP中利用XML技术构造远程服务(下)
2006/10/09 PHP
php 之 没有mysql支持时的替代方案
2006/10/09 PHP
PHP 远程关机实现代码
2009/11/10 PHP
PHP Cookie的使用教程详解
2013/06/03 PHP
php处理带有中文URL的方法
2016/07/11 PHP
解决thinkPHP 5 nginx 部署时,只跳转首页的问题
2019/10/16 PHP
prototype与jquery下Ajax实现的差别
2009/09/13 Javascript
javascript 事件绑定问题
2011/01/01 Javascript
jQuery将所有被选中的checkbox某个属性值连接成字符串的方法
2015/01/24 Javascript
ztree获取当前选中节点子节点id集合的方法
2015/02/12 Javascript
JavaScript合并两个数组并去除重复项的方法
2015/06/13 Javascript
Javascript编程中几种继承方式比较分析
2015/11/28 Javascript
浅谈Jquery中Ajax异步请求中的async参数的作用
2016/06/06 Javascript
jQuery+ajax实现滚动到页面底部自动加载图文列表效果(类似图片懒加载)
2016/06/07 Javascript
jQuery实现的自动加载页面功能示例
2016/09/04 Javascript
简单实现jQuery多选框功能
2017/01/09 Javascript
angularjs定时任务的设置与清除示例
2017/06/02 Javascript
基于vue监听滚动事件实现锚点链接平滑滚动的方法
2018/01/17 Javascript
babel之配置文件.babelrc入门详解
2018/02/22 Javascript
[05:31]DOTA2上海特级锦标赛主赛事第三日RECAP
2016/03/05 DOTA
在Python中使用matplotlib模块绘制数据图的示例
2015/05/04 Python
Java Web开发过程中登陆模块的验证码的实现方式总结
2016/05/25 Python
Django实现自定义404,500页面教程
2017/03/26 Python
Python编程实战之Oracle数据库操作示例
2017/06/21 Python
Python异常处理操作实例详解
2018/05/10 Python
Python3.5常见内置方法参数用法实例详解
2019/04/29 Python
Django 导出项目依赖库到 requirements.txt过程解析
2019/08/23 Python
python getpass实现密文实例详解
2019/09/24 Python
PyCharm永久激活方式(推荐)
2020/09/22 Python
三步解决python PermissionError: [WinError 5]拒绝访问的情况
2020/04/22 Python
基于python检查矩阵计算结果
2020/05/21 Python
详解python中的异常捕获
2020/12/15 Python
CSS3 @font-face属性使用指南
2014/12/12 HTML / CSS
Bally巴利中国官网:经典瑞士鞋履、手袋及配饰奢侈品牌
2018/10/09 全球购物
UNIX文件系统分类
2014/11/11 面试题
行政部工作岗位职责范本
2014/03/05 职场文书