天天看點

laravel 大檔案分片上傳

一、PHP實作大檔案上傳的辦法

1、因為預設會有上傳大小限制,所有可以通過修改nginx和PHP的配置,來擴大限制,但是隻能治标不治本,換一個環境還需要重新配置,而且很容易因為上傳逾時而中斷

2、通過将大檔案進行分片,每一片都是一個小檔案,每次上傳一部分到伺服器,通過多次上傳來完成大檔案的上傳功能,不會出現逾時中斷的問題

大檔案分片上傳解決的是大檔案上傳容易出現逾時中斷的問題,并不是解決上傳速度的問題

該篇文章是我在已有的可以正常運作的laravel項目中新增的幾個檔案來實作的,如果不想自己做也可以使用laravel的開源擴充包 AetherUpload - 讓 Laravel 支援超大檔案斷點上傳

二、具體實作

1、前端代碼,通過前端js代碼,實作每次隻上傳指定大小的檔案

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Ajax上傳檔案進度條顯示</title>
    <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js">
    </script>
    <script type="text/javascript">
        function sendfile(){
            const LENGTH = 2*1024*1024;
            var sta=0;
            var end=sta+LENGTH;
            var blob=new Blob();

            var fd=null;

            var xhr=null;

            var pic=document.getElementsByTagName('input')[0].files[0];
            console.log(pic,1);
            var name=pic.name;
            var  totalsize=pic.size;

            var precent=null;
            while(sta<totalsize){
                blob = pic.slice(sta,end);

                fd = new FormData();
                fd.append('part',blob);
                fd.append('name',name);

                xhr=new XMLHttpRequest();
                xhr.open('POST','{{route('upload.store')}}',false);

                xhr.send(fd);
                var response = JSON.parse(xhr.responseText);
                if (response.code == '-1'){
                    alert(response.error);
                    break;
                }


                precent=100 * (end/totalsize);
                if(precent>100){
                    precent=100;
                }
                console.log(Math.round(precent)+'%');
                document.getElementById('nei').style.width=precent+'%';
                document.getElementById('precent').innerHTML=Math.floor(precent)+'%';
                sta = end;
                end = end + LENGTH;
            }
        }
    </script>
    <style>
        #wai{
            width:500px;
            height:30px;
            border:1px solid green;
        }
        #nei{
            width:0px;
            height:30px;
            background:green;
        }
    </style>
</head>
<body>
<div id="wai">
    <div id="nei"></div>
</div>
<span id="precent"></span><br/>
<input type="file" name="pic" onchange="sendfile();"/>
</body>
</html>

           

2、後端代碼

2.1建立一個UploadContracts契約,用來約定一下接口

<?php


namespace App\Contracts;

use Illuminate\Http\Request;

interface UploadContracts
{
    /**
     * @param Request $request
     * @return mixed
     * 接收檔案
     */
    public function requestFile(Request $request);

    /**
     * @return mixed
     *  檔案校驗的方法
     */
    public function FileCheck();

    /**
     * @return mixed
     * 判斷目錄是否存在
     */
    public function ExistsDir();

    /**
     * @return mixed
     * 上傳檔案
     */
    public function UploadFIle();
}

           

2.2建立一個UploadSupport,實作這個契約接口

<?php


namespace App\Support;

use App\Contracts\UploadContracts;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;

class UploadSupport implements UploadContracts
{
    protected $file;

    protected $fileName;

    protected $fileSize;

    protected $fileExtension;

    protected $image = [
        'png','jpeg','jpg','gif'
    ];

    protected $txt = [
        'docx','doc'.'txt','csv','pdf'
    ];

    protected $url = 'default';

    /**
     * @var 本次檔案上傳位址
     */
    protected $pathUrl;

    public function requestFile(Request $request)
    {
        $this->file = $request->file('part');

        $this->fileName = $request->input('name');


        $this->fileSize = $this->file->getSize();

//        $this->fileExtension = $this->file->extension();

        $this->fileExtension = substr($this->fileName,strripos($this->fileName,'.') + 1);



        return $this;
    }

    public function FileCheck()
    {
        if (in_array($this->fileExtension,$this->image)){
            $this->url = 'images';
        }

        if (in_array($this->fileExtension,$this->txt)){
            $this->url = 'document';
        }

        $this->ExistsDir();

        $this->pathUrl = Storage::path('public').'/'.$this->url.'/'.$this->fileName;

        return $this;
    }

    public function ExistsDir()
    {
        if (!is_dir(Storage::path('public').'/'.$this->url)){
            mkdir(Storage::path('public').'/'.$this->url);
        }

        return true;
    }

    public function UploadFIle()
    {
		//分片檔案第一次上傳,建立檔案
        if (!file_exists($this->pathUrl)){

            return move_uploaded_file($this->file->path(),$this->pathUrl);
        }
		//追加分片檔案到之前的檔案中
        return file_put_contents($this->pathUrl,file_get_contents($this->file->path()),FILE_APPEND);

    }
}

           

3.3在AppServiceProvider服務提供者中注冊

laravel 大檔案分片上傳

3.4建立控制器,配置路由

<?php

namespace App\Http\Controllers\Admin\V1;

use App\Contracts\UploadContracts;
use Illuminate\Http\Request;

class UploadController extends BaseAdminController
{
    public function store(Request $request,UploadContracts $uploadContracts)
    {
        $upload = $uploadContracts->requestFile($request);
        $upload->FileCheck();
        $upload->UploadFIle();

        return response()->json([
            'code' => 200,
            'info' => '上傳成功'
        ]);
    }
}

           

路由

Route::view(‘upload’,‘upload’);

Route::post(‘UploadFile’,[\App\Http\Controllers\Admin\V1\UploadController::class,‘store’])->name(‘upload.store’)->middleware(‘upload.check’);

laravel 大檔案分片上傳
laravel 大檔案分片上傳
laravel 大檔案分片上傳