一、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服務提供者中注冊
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’);