php大文件上传

Posted 极客攻略

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了php大文件上传相关的知识,希望对你有一定的参考价值。

问题

服务端为什么不能直接传大文件?跟php.ini里面的几个配置有关:

1upload_max_filesize = 2M //PHP最大能接受的文件大小
2post_max_size = 8M //PHP能收到的最大POST值'
3memory_limit = 128M //内存上限
4max_execution_time = 30 //最大执行时间

当然不能简单粗暴的把上面几个值调大,否则服务器内存资源吃光是迟早的问题。

解决思路

好在html5开放了新的FILE API,也可以直接操作二进制对象,我们可以直接在浏览器端实现文件切割,按照以前的做法就得用Flash的方案,实现起来会麻烦很多。

JS思路

1.监听上传按钮的onchange事件

2.获取文件的FILE对象

3.把文件的FILE对象进行切割,并且附加到FORMDATA对象中

4.把FORMDATA对象通过AJAX发送到服务器

5.重复34步骤,直到文件发送完。

PHP思路

1.建立上传文件夹

2.把文件从上传临时目录移动到上传文件夹

3.所有的文件块上传完成后,进行文件合成

4.删除文件夹

5.返回上传后的文件路径

DEMO代码

前端部分代码

  1<!doctype html>
 2<html lang="en">
 3<head>
 4    <meta charset="UTF-8">
 5    <meta name="viewport"
 6          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">

 7    <meta http-equiv="X-UA-Compatible" content="ie=edge">
 8    <title>Document</title>
 9    <style>
10        #progress{
11            width300px;
12            height20px;
13            background-color:#f7f7f7;
14            box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);
15            border-radius:4px;
16            background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);
17        }
18
19        #finish{
20            background-color#149bdf;
21            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);
22            background-size:40px 40px;
23            height100%;
24        }
25        form{
26            margin-top50px;
27        }
28    
</style>
29</head>
30<body>
31<div id="progress">
32    <div id="finish" style="width: 0%;" progress="0"></div>
33</div>
34<form action="./upload.php">
35    <input type="file" name="file" id="file">
36    <input type="button" value="停止" id="stop">
37</form>
38<script>
39    var fileForm = document.getElementById("file");
40    var stopBtn = document.getElementById('stop');
41    var upload = new Upload();
42
43    fileForm.onchange = function(){
44        upload.addFileAndSend(this);
45    }
46
47    stopBtn.onclick = function(){
48        this.value = "停止中";
49        upload.stop();
50        this.value = "已停止";
51    }
52
53    function Upload(){
54        var xhr = new XMLHttpRequest();
55        var form_data = new FormData();
56        const LENGTH = 1024 * 1024;
57        var start = 0;
58        var end = start + LENGTH;
59        var blob;
60        var blob_num = 1;
61        var is_stop = 0
62        //对外方法,传入文件对象
63        this.addFileAndSend = function(that){
64            var file = that.files[0];
65            blob = cutFile(file);
66            sendFile(blob,file);
67            blob_num  += 1;
68        }
69        //停止文件上传
70        this.stop = function(){
71            xhr.abort();
72            is_stop = 1;
73        }
74        //切割文件
75        function cutFile(file){
76            var file_blob = file.slice(start,end);
77            start = end;
78            end = start + LENGTH;
79            return file_blob;
80        };
81        //发送文件
82        function sendFile(blob,file){
83            var total_blob_num = Math.ceil(file.size / LENGTH);
84            form_data.append('file',blob);
85            form_data.append('blob_num',blob_num);
86            form_data.append('total_blob_num',total_blob_num);
87            form_data.append('file_name',file.name);
88
89            xhr.open('POST','./upload.php',false);
90            xhr.onreadystatechange  = function () {
91                var progress;
92                var progressObj = document.getElementById('finish');
93                if(total_blob_num == 1){
94                    progress = '100%';
95                }else{
96                    progress = Math.min(100,(blob_num/total_blob_num)* 100 ) +'%';
97                }
98                progressObj.style.width = progress;
99                var t = setTimeout(function(){
100                    if(start < file.size && is_stop === 0){
101                        blob = cutFile(file);
102                        sendFile(blob,file);
103                        blob_num  += 1;
104                    }else{
105                        setTimeout(t);
106                    }
107                },1000);
108            }
109            xhr.send(form_data);
110        }
111    }
112
113
</script>
114</body>
115</html>

PHP部分代码

 1<?php
2class Upload{
3    private $filepath = './upload'//上传目录
4    private $tmpPath;  //PHP文件临时目录
5    private $blobNum; //第几个文件块
6    private $totalBlobNum; //文件块总数
7    private $fileName; //文件名
8
9    public function __construct($tmpPath,$blobNum,$totalBlobNum,$fileName){
10        $this->tmpPath =  $tmpPath;
11        $this->blobNum =  $blobNum;
12        $this->totalBlobNum =  $totalBlobNum;
13        $this->fileName =  $fileName;
14
15        $this->moveFile();
16        $this->fileMerge();
17    }
18
19    //判断是否是最后一块,如果是则进行文件合成并且删除文件块
20    private function fileMerge(){
21        if($this->blobNum == $this->totalBlobNum){
22            $blob = '';
23            for($i=1; $i<= $this->totalBlobNum; $i++){
24                $blob .= file_get_contents($this->filepath.'/'$this->fileName.'__'.$i);
25            }
26            file_put_contents($this->filepath.'/'$this->fileName,$blob);
27           $this->deleteFileBlob();
28        }
29    }
30
31   //删除文件块
32    private function deleteFileBlob(){
33        for($i=1; $i<= $this->totalBlobNum; $i++){
34            @unlink($this->filepath.'/'$this->fileName.'__'.$i);
35        }
36    }
37
38    //移动文件
39    private function moveFile(){
40        $this->touchDir();
41        $filename = $this->filepath.'/'$this->fileName.'__'.$this->blobNum;
42        move_uploaded_file($this->tmpPath,$filename);
43    }
44
45    //API返回数据
46    public function apiReturn(){
47        if($this->blobNum == $this->totalBlobNum){
48                if(file_exists($this->filepath.'/'$this->fileName)){
49                    $data['code'] = 2;
50                    $data['msg'] = 'success';
51                    $data['file_path'] = 'http://'.$_SERVER['HTTP_HOST'].dirname($_SERVER['DOCUMENT_URI']).str_replace('.','',$this->filepath).'/'$this->fileName;
52                }
53        }else{
54                if(file_exists($this->filepath.'/'$this->fileName.'__'.$this->blobNum)){
55                    $data['code'] = 1;
56                    $data['msg'] = 'waiting for all';
57                    $data['file_path'] = '';
58                }
59        }
60        header('Content-type: application/json');
61        echo json_encode($data);
62    }
63
64    //建立上传文件夹
65    private function touchDir(){
66        if(!file_exists($this->filepath)){
67            return mkdir($this->filepath);
68        }
69    }
70}
71
72//实例化并获取系统变量传参
73$upload = new Upload($_FILES['file']['tmp_name'],$_POST['blob_num'],$_POST['total_blob_num'],$_POST['file_name']);
74//调用方法,返回结果
75$upload->apiReturn();
76

PHP后台处理

  1/**
 2     * 大文件切片上传
 3     */

 4    public function VideoUpload(){
 5        header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
 6        header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
 7        header("Cache-Control: no-store, no-cache, must-revalidate");
 8        header("Cache-Control: post-check=0, pre-check=0"false);
 9        header("Pragma: no-cache");
10        if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
11            exit// finish preflight CORS requests here
12        }
13        if ( !empty($_REQUEST[ 'debug' ]) ) {
14            $random = rand(0, intval($_REQUEST[ 'debug' ]) );
15            if ( $random === 0 ) {
16                header("HTTP/1.0 500 Internal Server Error");
17                exit;
18            }
19        }
20        // header("HTTP/1.0 500 Internal Server Error");
21        // exit;
22        // 5 minutes execution time
23        @set_time_limit(5 * 60);
24        // Uncomment this one to fake upload time
25        // usleep(5000);
26        // Settings
27        // $targetDir = ini_get("upload_tmp_dir") . DIRECTORY_SEPARATOR . "plupload";
28        $targetDir = './Fadonghui/College/uploadfile/cache/';
29        $uploadDir = './Fadonghui/College/uploadfile/cache/';
30        $cleanupTargetDir = true// 开启文件缓存删除
31        $maxFileAge = 60*60*24// 文件缓存时间超过时间自动删除
32        // 验证缓存目录是否存在不存在创建
33        if (!file_exists($targetDir)) {
34            @mkdir($targetDir);
35        }
36        // 验证缓存目录是否存在不存在创建
37        if (!file_exists($uploadDir)) {
38            @mkdir($uploadDir);
39        }
40        // Get 或 file 方式获取文件名
41        if (isset($_REQUEST["name"])) {
42            $fileName = $_REQUEST["name"];
43        } elseif (!empty($_FILES)) {
44            $fileName = $_FILES["file"]["name"];
45        } else {
46            $fileName = uniqid("file_");
47        }
48        $oldName = $fileName;//记录文件原始名字
49        $filePath = $targetDir . $fileName;
50        // $uploadPath = $uploadDir . DIRECTORY_SEPARATOR . $fileName;
51        // Chunking might be enabled
52        $chunk = isset($_REQUEST["chunk"]) ? intval($_REQUEST["chunk"]) : 0;
53        $chunks = isset($_REQUEST["chunks"]) ? intval($_REQUEST["chunks"]) : 1;
54        // 删除缓存校验
55        if ($cleanupTargetDir) {
56            if (!is_dir($targetDir) || !$dir = opendir($targetDir)) {
57                die('{"jsonrpc" : "2.0", "error" : {"code": 100, "message": "Failed to open temp directory."}, "id" : "id"}');
58            }
59            while (($file = readdir($dir)) !== false) {
60                $tmpfilePath = $targetDir  . $file;
61                // If temp file is current file proceed to the next
62                if ($tmpfilePath == "{$filePath}_{$chunk}.part" || $tmpfilePath == "{$filePath}_{$chunk}.parttmp") {
63                    continue;
64                }
65                // Remove temp file if it is older than the max age and is not the current file
66                if (preg_match('/\.(part|parttmp|mp4)$/', $file) && (@filemtime($tmpfilePath) < time() - $maxFileAge)) {
67                    @unlink($tmpfilePath);
68                }
69            }
70            closedir($dir);
71        }
72        // 打开并写入缓存文件
73        if (!$out = @fopen("{$filePath}_{$chunk}.parttmp""wb")) {
74            die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}');
75        }
76        if (!empty($_FILES)) {
77            if ($_FILES["file"]["error"] || !is_uploaded_file($_FILES["file"]["tmp_name"])) {
78                die('{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "id" : "id"}');
79            }
80            // Read binary input stream and append it to temp file
81            if (!$in = @fopen($_FILES["file"]["tmp_name"], "rb")) {
82                die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
83            }
84        } else {
85            if (!$in = @fopen("php://input""rb")) {
86                die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
87            }
88        }
89        while ($buff = fread($in, 4096)) {
90            fwrite($out, $buff);
91        }
92        @fclose($out);
93        @fclose($in);
94        rename("{$filePath}_{$chunk}.parttmp""{$filePath}_{$chunk}.part");
95        $index = 0;
96        $done = true;
97        for( $index = 0; $index < $chunks; $index++ ) {
98            if ( !file_exists("{$filePath}_{$index}.part") ) {
99                $done = false;
100                break;
101            }
102        }
103        //文件全部上传 执行合并文件
104        if ( $done ) {
105            $pathInfo = pathinfo($fileName);
106            $hashStr = substr(md5($pathInfo['basename']),8,16);
107            $hashName = time() . $hashStr . '.' .$pathInfo['extension'];
108            $uploadPath = $uploadDir .$hashName;
109            if (!$out = @fopen($uploadPath, "wb")) {
110                die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}');
111            }
112            if ( flock($out, LOCK_EX) ) {
113                for( $index = 0; $index < $chunks; $index++ ) {
114                    if (!$in = @fopen("{$filePath}_{$index}.part""rb")) {
115                        break;
116                    }
117                    while ($buff = fread($in, 4096)) {
118                        fwrite($out, $buff);
119                    }
120                    @fclose($in);
121                    @unlink("{$filePath}_{$index}.part");
122                }
123                flock($out, LOCK_UN);
124            }
125            @fclose($out);
126            /*/腾讯云--对象存储
127            vendor('TencentYun/TencentYunSendFile');
128            $Yun = new \TencentYunSendFile();
129            $res = $Yun->uploadYun(array('tmp_name'=>$uploadPath,'name'=>$hashName),'video');
130            var_dump($res);*/

131            //引用第三方类库
132            /*vendor('getid3.getid3');
133            $getID3 = new \getID3(); //实例化类
134            $ThisFileInfo = $getID3->analyze($uploadPath);//分析文件*/

135            $response = array(
136                'success'=>true,
137                'oldName'=>$oldName,
138                //'filePaht'=>$hashName,
139                //'filePaht'=>substr($uploadPath,1),
140                'fileSuffixes'=>$pathInfo['extension'],
141                'time' =>$ThisFileInfo['playtime_string'],
142            );
143            //删除源文件
144            /*unlink($uploadPath);*/
145            die(json_encode($response));
146        }
147        // Return Success JSON-RPC response
148        die('{"jsonrpc" : "2.0", "result" : null, "id" : "id"}');
149    }

 欢迎订阅


以上是关于php大文件上传的主要内容,如果未能解决你的问题,请参考以下文章

文件/大文件上传功能实现(JS+PHP)全过程

PHP 快速实现大文件上传

PHP 大文件上传,支持断点续传,求具体方案、源码或者文件上传插件

php上传大文件的解决方案

php实现大文件上传带进度条

js+php分片上传大文件组件