php+js实现文件分块上传

贾蔷
• 阅读 3136

我们在上传大文件时,可能会由于服务器的原因导致文件上传失败,文件过大时由于服务器的配置或响应事件过长导致上传文件失败,这时候我们可以将一个大的文件分为若干块,然后分批次上传到服务端,当所有文件块上传完成后再由服务器将各个文件块整合成我们上传的文件

一:分块上传流程:

1:由前端js将上传的文件信息进行切割成若干块,然后循环将若干块的文件块上传到服务端

2:服务端接收到文件块信息后保存起来,当所有文件块上传完毕后,将所有上传的文件块整合成文件并保存起来

二:实现代码:

1:HTML

<input type="file" id="file">
<input type="button" id="upload" value="上传">
<input type="button" id="stop" value="停止">
<input type="button" id="restart" value="继续上传">
上传进度:<span id="progress"></span>

2:JS

//获取节点
var fileForm = document.getElementById("file");
var uploadBtn = document.getElementById('upload');
var stopBtn = document.getElementById('stop');
var restartBtn = document.getElementById('restart');
//定义常量
const LENGTH = 100 * 1024;//每个上传的文件块大小(100KB)
var start = 0;
var end = LENGTH + start;
var blob;
var is_stop = 0;
var blob_num = 1;
var file = null;
var upload_instance = new Upload();
//上传事件
uploadBtn.onclick = function () {
   upload_instance.addFileAndSend(fileForm);
   return false;
}
stopBtn.onclick = function () {
   upload_instance.stop();
   return false;
}
restartBtn.onclick = function () {
   upload_instance.start();
   return false;
}
function Upload() {
    //判断浏览器类型
    if (window.XMLHttpRequest){
        //IE7+, Firefox, Chrome, Opera, Safari
        var xhr=new XMLHttpRequest();
    }else{
        //IE6, IE5
        var xhr=new ActiveXObject("Microsoft.XMLHTTP");
    }
   //上传文件
   this.addFileAndSend = function (that) {
       file = that.files[0];
       blob = cutFile(file);
       //上传
       uploadFile(blob, file);
       blob_num += 1;
   }
   //停止文件上传
   this.stop = function () {
       xhr.abort();
       is_stop = 1;
   }
   this.start = function () {
       uploadFile(blob, file);
       is_stop = 0;
   }
   //切割文件
   function cutFile(file) {
       var file_blob = file.slice(start, end);
       start = end;
       end = start + LENGTH;
       return file_blob;
   };
    //上传文件
    function uploadFile(blob, file) {
        var form_data = new FormData();
        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', './test.php', false);
        xhr.onreadystatechange = function () {
            //获取上传进度
            if (total_blob_num == 1) {
                progressText = '100%';
            } else {
                progressText = (Math.min(100, (blob_num / total_blob_num) * 100)).toFixed(2) + '%';
            }
            var progress = document.getElementById('progress');
            progress.innerHTML = progressText;
            
            //循环执行上传,直到所有文件块上传完成
            var t = setTimeout(function () {
                if (start < file.size && is_stop == 0) {
                    blob = cutFile(file);
                    uploadFile(blob, file);
                    blob_num += 1;
                } else {
                    //所有文件块上传完成
                }
            }, 1000);
        }
        xhr.send(form_data);
        //每次文件块上传后,清空上传信息
        form_data = "";
    }
}

3:PHP

(1):上传类:

class Upload
{
    /**
     * @var string 上传目录
     */
    private $filepath = './upload'; //上传目录
    /**
     * @var string 块文件临时存储的位置
     */
    private $tmpPath;
    /**
     * @var integer 第几个文件块
     */
    private $blobNum;
    /**
     * @var integer //文件块总数
     */
    private $totalBlobNum;
    /**
     * @var string 上传文件名
     */
    private $fileName;
    public function __construct($tmpPath, $blobNum,$totalBlobNum,$fileName, $filepath = ''){
        if (!empty($filepath)) {
            $this->filepath = $filepath;
        }
        $this->tmpPath = $tmpPath;
        $this->blobNum = $blobNum;
        $this->totalBlobNum = $totalBlobNum;
        $this->fileName = $fileName;
        //保存文件块
        $this->moveFile();
        //保存文件
        $this->fileMerge();
    }
    private function fileMerge(){
        //当文件块都上传后将文件块整合成文件
        if($this->blobNum == $this->totalBlobNum){
            for($i=1; $i<= $this->totalBlobNum; $i++){
                $blob = '';
                $blob = file_get_contents($this->filepath.'/'. $this->fileName.'__'.$i);
                file_put_contents($this->filepath.'/'. $this->fileName, $blob, FILE_APPEND );
                unset($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);
    }
    //上传返回
    public function uploadReturn(){
        if($this->blobNum == $this->totalBlobNum){
            if(file_exists($this->filepath.'/'. $this->fileName)){
                return [
                    'code' => 2,
                    'message' => 'success',
                    'file_path' => 'http://'.$_SERVER['HTTP_HOST'].str_replace('.','',$this->filepath).'/'. $this->fileName,
                    'local_path' => str_replace('.','',$this->filepath).'/'. $this->fileName
                ];
            }
        }
        return [
            'code' => 1,
            'message' => 'waiting',
        ];
    }
    /**
     * 创建目录
     */
    private function touchDir(){
        if(!file_exists($this->filepath)){
            return mkdir($this->filepath);
        }
    }
}

调用上传类

$tmpName = $_FILES['file']['tmp_name'];
$blobNum = $_POST['blob_num'];
$totalBlobNum = $_POST['total_blob_num'];
$fileName = $_POST['file_name'];
$upload = new Upload($tmpName, $blobNum, $totalBlobNum, $fileName);
$data = $upload->uploadReturn();
header('Content-type: application/json');
return json_encode($data);

根据如上步骤就可以实现将文件分成若干块进行上传功能

点赞
收藏
评论区
推荐文章
不才 不才
4年前
大文件分块(切片)断点上传
之前看过相关文章但是一直没有动手实现,这个东西就是为了实现这个而产生的。前端流程图主要技术点切片1.利用Blob.prototype.slice切片2.获取切片md5作为唯一标识具体代码JavaScript//计算切片数量constpageMath.ceil(file.size/size);//
Wesley13 Wesley13
3年前
java日常工作错误总结
1.将一个新的项目拷贝到另一台电脑上,放入tomcat中运行找不到路径,报错404、重新创建一个servlet运行就可以正常访问到。2.但上传的文件过大时上传文件会报404错误把<propertyname"maxUploadSize"value"50000000"/限制该大些就能正常上传文件<beanid"multipartReso
皕杰报表(关于如何上传和下载文件到数据库)
在皕杰报表中文件是否可以上传到数据库中,当然是可以的。然后在附件上传和下载中,设置相对路径或绝对路径,文件名称,文件类型和上传的空值条件(上传的大小,默认限制是5120kb和满足什么条件时上传)。在下载中选择相对路径或绝对路径,填写下载链接名称和下载文件名称。填报操作时有三个函数:filedata、filename、filepath。filedata:获取文
李志宽 李志宽
2年前
【小白必学】文件上传的漏洞介绍及常见防御方法
01文件上传漏洞原理在文件上传的功能处,若服务端脚本语言未对上传的文件进行严格验证和过滤,导致恶意用户上传恶意的脚本文件时,就有可能获取执行服务端命令的能力,这就是文件上传漏洞。02文件上传漏洞触发点相册、头像上传、视频、照片分享、附件上传(论坛发帖、邮箱
Stella981 Stella981
3年前
RestTemplate OR Spring Cloud Feign 上传文件
SpringBoot,通过RestTemplate或者SpringCloudFeign,上传文件(支持多文件上传),服务端接口是MultipartFile接收。将文件的字节流,放入ByteArrayResource中,并重写getFilename方法。然后将ByteArrayResource放入MultiValueMap中(如果是Feign调
Wesley13 Wesley13
3年前
PHP 文件上传的原理及案例分析
原理将客户端文件上传至服务器端,在服务器端临时存储,再将服务器端临时存储的文件移至指定位置实现文件上传需要的知识点:前端页面1.form表单必须是用post发送方式,因为get会将参数带到url中,而上传的文件转换后字符会很长,而且也是为了安全性2.form表单需要使用enctype
Wesley13 Wesley13
3年前
JS 异步分段上传文件
为了解决大文件上传(PHP上传最大限制2GB)同时为了解决文件上传是对服务器造成的压力可以通过分段上传解决这个问题,这得益于HTML5开发的fileAPI前台代码:引用了进度条插件myProgress.js<linkhref"__PUBLIC__/admin/css/myProgress.css"rel"stylesh
Wesley13 Wesley13
3年前
Git之提交错误:RPC failed; result=22, HTTP code = 411
这个问题刚开始,我一直以为是服务器有问题,后来发现是Git的最大文件限制,git的配置中http.postBuffer默认上限为1M所致问题上传报错,导致这个问题的原因是上传的文件超过了默认的配置上传的文件大小。error:RPCfailed;HTTP411curl22TherequestedURLret
Wesley13 Wesley13
3年前
PHP代码审计笔记
0x01最简单的文件上传未进行文件类型和格式做合法性校验,任意文件上传漏洞代码示例:新建一个提供上传文件的 upload.html<html创建上传脚本  upload\_file.php<?php漏洞利用:可上传任意文件!(https://oscimg.oschina.net/oscnet/a
Wesley13 Wesley13
3年前
Java多线程导致的的一个事物性问题
业务场景我们现在有一个类似于文件上传的功能,各个子站点接受业务,业务上传文件,各个子站点的文件需要提交到总站点保存,文件是按批次提交到总站点的,也就是说,一个批次下面约有几百个文件。      考虑到白天提交这么多文件会影响到子站点其他系统带宽,我们将分站点的文件提交到总站点这个操作过程独立出来,放到晚上来做,具体时间是晚上7:00到早上7:00。
h4ckb0ss h4ckb0ss
1年前
文件上传(一):PortSwigger靶场通关笔记
文件上传漏洞通常指应用对用户上传的文件没有完善的检验,允许攻击者通过Web应用程序上传恶意文件到服务器,然后通过这些恶意文件来进行执行任意代码,在客户端影响用户等攻击
贾蔷
贾蔷
Lv1
墙里秋千墙外道。墙外行人,墙里佳人笑。笑渐不闻声渐悄,多情却被无情恼。
文章
6
粉丝
0
获赞
0