PHP 分片上傳檔案

xiaosheng發表於2021-04-18

注: 本文大致上是借鑑於社群的這篇文章, 大檔案傳輸解決方案:分片上傳 / 下載限速


  • html:
<!DOCTYPE html>
<html>
<head>
    <title></title>
    <style type="text/css">
        #progress{
            display: inline-block;
            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;
            display: inline-block;
            height: 20px;
        }
    </style>
</head>
<body>
    <input type="file" style="width: 200px;" id="upload" />
    <span id="progress" style="text-align: left;">
        <span id="finish" style="width: 0%;" progress="0"></span>
    </span>
</body>
</html>

javascript:

let upload = document.getElementById("upload");

var uploadObj = new Upload();

upload.onchange = function(){
    uploadObj.addFileAndSend(this,'finish','ppt');
}

function Upload(){
    var xhr = new XMLHttpRequest();
    var form_data = new FormData();
    const LENGTH = 1024 * 1024 *2;
    var start = 0;
    var end = start + LENGTH;
    var blob;
    var blob_num = 1;
    var is_stop = 0;

    //對外方法,傳入檔案物件
    // that 是input物件, idName 是進度條id名稱, type 是上傳檔案分類, 可以用此來區分上傳檔案目錄
    this.addFileAndSend = function(that,idName,type){
        var file = that.files[0];
        blob = cutFile(file);
        sendFile(blob,file,idName,that,type);
        blob_num  += 1;
    }

    //切割檔案
    function cutFile(file){
        var file_blob = file.slice(start,end);
        start = end;
        end = start + LENGTH;
        return file_blob;
    };

    //傳送檔案
    function sendFile(blob,file,idName,uploadObj,type){
        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);
        form_data.append('file_type',type);
        xhr.open('POST','後端上傳路由',false);

        xhr.onreadystatechange  = function () {
            if (xhr.readyState==4 && xhr.status==200){
                let responseMessage = JSON.parse(xhr.responseText);
                if(responseMessage['code'] == 1 && (type == 'ppt' || type == 'word')) {                    
                    $(uploadObj).attr('data',responseMessage['data'])
                    $('#' + idName).append(responseMessage['msg'])
                } else if (responseMessage['code'] == 1 && type == 'avatar') {
                    $('#' + idName).css('display', 'block');
                    $('#' + idName).attr('src',responseMessage['data'])
                }
            }

            if(type == 'ppt' || type == 'word') {
                var progress;
                var progressObj = document.getElementById(idName);
                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,idName,uploadObj,type);
                    blob_num  += 1;
                }else{
                    setTimeout(t);
                }
            },1000);
        }
        xhr.send(form_data);
    }
}

php:

public function upload()
{
    $file  =  $_FILES['file'];
    $blobNum  =  $_POST['blob_num'];
    $totalBlobNum  =  $_POST['total_blob_num'];
    $fileName  =  $_POST['file_name'];
    $fileType  =  $_POST['file_type'];
    $fileExtension  =  pathinfo($fileName, PATHINFO_EXTENSION);
    $tempName  =  $_FILES['file']['tmp_name']; //臨時檔案的絕對路徑

    $pathPrefix  =  './Upload/'  .  $fileType;
    if(!file_exists($pathPrefix)) {
        mkdir($pathPrefix,0777);
    }
    chmod($pathPrefix,0777);

    $filePath  =  $pathPrefix  .  '/'  .  $fileName  .  '_'  .  $blobNum;
    $result  =  move_uploaded_file($tempName,$filePath);
    $renameResult  =  '';

    if($blobNum  ==  $totalBlobNum){
        $blob  =  '';

        for($i=1; $i<=  $totalBlobNum; $i++){
            $blob  .=  file_get_contents($pathPrefix  .  '/'  .  $fileName  .'_'  .  $i);
        }
        file_put_contents($pathPrefix  .  '/'  .$fileName,$blob);
        //合併完刪除檔案塊
        for($i=1; $i<=  $totalBlobNum; $i++){
            unlink($pathPrefix  .  '/'  .  $fileName  .'_'.$i);
        }

        $newFileName  =  $pathPrefix  .  '/'  .  time() .  '.'  .  $fileExtension;
        $renameResult  =  rename($pathPrefix  .  '/'  .$fileName,$newFileName);
    }

    $newFileName  =  substr($newFileName,1);
    if ($renameResult){
        return [
            'code'  =>  1,
            'msg'  =>  'upload success',
            'data'  =>  $newFileName
        ];
    }else{
        return [
            'code'  =>  0,
            'msg'  =>  'upload failed'
        ];
    }
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章