4
0
Fork 0
dcat-admin/src/Support/WebUploader.php

255 lines
5.7 KiB
PHP

<?php
namespace Dcat\Admin\Support;
use Illuminate\Http\Request;
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* WebUploader文件上传处理.
*
* @property string $_id
* @property int $chunk
* @property int $chunks
* @property string $upload_column
* @property UploadedFile $file
*/
class WebUploader
{
const FILE_NAME = '_file_';
public $temporaryDirectory = 'tmp';
protected $temporaryFilePath;
protected $completeFile;
public function __construct(Request $request = null)
{
$request = $this->prepareRequest($request ?: request());
$this->_id = $request->get('_id');
$this->chunk = $request->get('chunk');
$this->chunks = $request->get('chunks');
$this->upload_column = $request->get('upload_column');
$this->file = $request->file(static::FILE_NAME);
}
protected function prepareRequest($request)
{
$relation = $request->get('_relation');
if (! $relation || ! is_string($relation)) {
return $request;
}
return $request->merge([
'_relation' => trim($relation, ','),
]);
}
/**
* 判断是否是分块上传.
*
* @return bool
*/
public function hasChunkFile()
{
return $this->chunks > 1;
}
/**
* 判断是否是文件上传请求.
*
* @return bool
*/
public function isUploading()
{
$file = $this->file;
if (
! $file
|| ! $this->upload_column
|| ! $file instanceof UploadedFile
) {
return false;
}
return true;
}
/**
* 获取完整的上传文件.
*
* @return UploadedFile|void
*/
public function getUploadedFile()
{
$file = $this->file;
if (! $file || ! $file instanceof UploadedFile) {
return;
}
if (! $this->hasChunkFile()) {
return $file;
}
if ($this->completeFile !== null) {
return $this->completeFile;
}
return $this->completeFile = $this->mergeChunks($file);
}
/**
* 移除临时文件以及文件夹.
*/
public function deleteTemporaryFile()
{
if (! $this->temporaryFilePath) {
return;
}
@unlink($this->temporaryFilePath);
if (
! Finder::create()
->in($dir = dirname($this->temporaryFilePath))
->files()
->count()
) {
@rmdir($dir);
}
}
/**
* 合并分块文件.
*
* @param UploadedFile $file
* @return UploadedFile|false
*/
protected function mergeChunks(UploadedFile $file)
{
$tmpDir = $this->getTemporaryPath($this->_id);
$newFilename = $this->generateChunkFileName($file);
// 移动当前分块到临时目录.
$this->moveChunk($file, $tmpDir, $newFilename);
// 判断所有分块是否上传完毕.
if (! $this->isComplete($tmpDir, $newFilename)) {
return false;
}
$this->temporaryFilePath = $tmpDir.'/'.$newFilename.'.tmp';
$this->putTempFileContent($this->temporaryFilePath, $tmpDir, $newFilename);
return new UploadedFile(
$this->temporaryFilePath,
$file->getClientOriginalName(),
null,
null,
true
);
}
/**
* 判断所有分块是否上传完毕.
*
* @param string $tmpDir
* @param string $newFilename
* @return bool
*/
protected function isComplete($tmpDir, $newFilename)
{
for ($index = 0; $index < $this->chunks; $index++) {
if (! is_file("{$tmpDir}/{$newFilename}.{$index}.part")) {
return false;
}
}
return true;
}
/**
* 移动分块文件到临时目录.
*
* @param UploadedFile $file
* @param string $tmpDir
* @param string $newFilename
*/
protected function moveChunk(UploadedFile $file, $tmpDir, $newFilename)
{
$file->move($tmpDir, "{$newFilename}.{$this->chunk}.part");
}
/**
* @param string $path
* @param string $tmpDir
* @param string $newFilename
*/
protected function putTempFileContent($path, $tmpDir, $newFileame)
{
$out = fopen($path, 'wb');
if (flock($out, LOCK_EX)) {
for ($index = 0; $index < $this->chunks; $index++) {
$partPath = "{$tmpDir}/{$newFileame}.{$index}.part";
if (! $in = @fopen($partPath, 'rb')) {
break;
}
while ($buff = fread($in, 4096)) {
fwrite($out, $buff);
}
@fclose($in);
@unlink($partPath);
}
flock($out, LOCK_UN);
}
fclose($out);
}
/**
* 生成分块文件名称.
*
* @param UploadedFile $file
* @return string
*/
protected function generateChunkFileName(UploadedFile $file)
{
return md5($file->getClientOriginalName());
}
/**
* 获取临时文件路径.
*
* @param mixed $path
* @return string
*/
public function getTemporaryPath($path)
{
return $this->getTemporaryDirectory().'/'.$path;
}
/**
* 获取临时文件目录.
*
* @return string
*/
public function getTemporaryDirectory()
{
$dir = storage_path($this->temporaryDirectory);
if (! is_dir($dir)) {
app('files')->makeDirectory($dir, 0755, true);
}
return rtrim($dir, '/');
}
}