$mergin
$mergin : bool
分片上传是否需要合并
文件上传类 Class Upload
$mergin : bool
分片上传是否需要合并
$file : \app\api\model\File
<?php
namespace lib;
use app\api\model\File;
use app\api\model\FileType;
use app\common\controller\Util;
use think\Exception;
/**
* 文件上传类
* Class Upload
* @package lib
*/
class Upload
{
/**
* 分片上传是否需要合并
* @var bool
*/
protected $mergin = false;
protected $config = [];
/**
* @var File
*/
protected $file = null;
protected $fileInfo = null;
protected $tid = null;
public function __construct($file = null, $tid = null)
{
$this->config = config('upload');
if ($file) {
$this->setFile($file);
}
if ($tid) {
$this->setTid($tid);
}
}
/**
* 获取文件
* @return File|null
*/
public function getFile(): File
{
return $this->file;
}
/**
* 设置类别id
* @param int $tid
* @throws Exception
*/
public function setTid($tid = 0)
{
$tid = $tid == -1 ? 0 : $tid;
// 校验分类id不存在
$checkTidExist = FileType::get(['id' => $tid]);
if (empty($checkTidExist) && $tid != 0) {
throw new Exception('附件分类不存在!');
}
$this->tid = $tid;
}
/**
* 设置文件
* @param $file
* @throws Exception
*/
public function setFile($file)
{
// 判断文件是否为空
if (empty($file)) {
throw new Exception('未选择文件或文件超出服务器限制!');
}
$fileInfo = $file->getInfo();
$suffix = strtolower(pathinfo($fileInfo['name'], PATHINFO_EXTENSION));
$suffix = $suffix && preg_match("/^[a-zA-Z0-9]+$/", $suffix) ? $suffix : 'file';
$fileInfo['suffix'] = $suffix;
$this->file = $file;
$this->fileInfo = $fileInfo;
$this->checkDisabledFile();
$this->checkSize();
}
/**
* 检测上传文件是否为php和html文件
* @return bool
* @throws Exception
*/
public function checkDisabledFile(): bool
{
// 判断上传文件是否属于禁止上传
if (in_array($this->fileInfo['type'], [$this->config['disabledFileType']])
|| in_array($this->fileInfo['suffix'], [$this->config['disabledSuffix']])
|| preg_match("/^php(.*)/i", $this->fileInfo['suffix'])) {
throw new Exception('禁止上传该类型文件!');
}
return true;
}
/**
* 检测文件类型
* @return bool
* @throws Exception
*/
public function checkMimeType(): bool
{
// 获取可上传的文件类型
$mimeTypeArr = explode(',', strtolower($this->config['mimeType']));
// 获取上传的文件类型
$typeArr = explode('/', $this->fileInfo['type']);
// 判断mimetype值是否正确
if (stripos($this->fileInfo['type'], '/') === false) {
throw new Exception('上传的文件格式不正确!');
}
// 校验文件后缀是否合法
if ($this->config['mimeType'] === '*'
|| in_array($this->fileInfo['suffix'], $mimeTypeArr)
|| in_array('.' . $this->fileInfo['suffix'], $mimeTypeArr)
|| in_array($typeArr[0] . "/*", $mimeTypeArr)
|| in_array($this->fileInfo['type'], $mimeTypeArr)
&& stripos($this->fileInfo['type'], '/' !== false)
) {
return true;
}
throw new Exception('上传的文件格式不正确!');
}
/**
* 检测文件大小是否超出限制
* @throws Exception
*/
public function checkSize()
{
// 校验文件大小配置
preg_match('/([0-9\.]+)(\w+)/', $this->config['maxsize'], $matches);
// 获取文件大小
$size = $matches ? $matches[1] : $this->config['maxsize'];
// 获取大小单位
$type = $matches ? strtolower($matches[2]) : 'b';
// 大小单位字典
$typeDict = [
'b' => 0,
'k' => 1,
'kb' => 1,
'm' => 2,
'mb' => 2,
'gb' => 3,
'g' => 3
];
// 计算大小
$size = (int)($size * pow(1024, isset($typeDict[$type]) ? $typeDict[$type] : 0));
if ($this->fileInfo['size'] > $size) {
throw new Exception('上传文件大小超出服务器限制!');
}
}
/**
* 获取文件后缀
* @return string
*/
public function getSuffix(): string
{
return $this->fileInfo['suffix'] ?: 'file';
}
/**
* 获取存储的文件名
* @param null $savekey
* @param null $filename
* @param null $md5
* @return mixed|string|string[]
*/
public function getSavekey($savekey = null, $filename = null, $md5 = null)
{
if ($filename) {
$suffix = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
$suffix = $suffix && preg_match("/^[a-zA-Z0-9]+$/", $suffix) ? $suffix : '';
} else {
$suffix = $this->fileInfo['suffix'];
}
$filename = $filename ? $filename : ($suffix ? substr($this->fileInfo['name'], 0, strripos($this->fileInfo['name'], '.')) : $this->fileInfo['name']);
$md5 = $md5 ? $md5 : md5_file($this->fileInfo['tmp_name']);
$replaceArr = [
'{year}' => date('Y'),
'{mon}' => date('m'),
'{day}' => date('d'),
'{hour}' => date('H'),
'{min}' => date('i'),
'{sec}' => date('s'),
'{random}' => Util::random(16),
'{random32}' => Util::random(32),
'{filename}' => substr($filename, 0, 100),
'{suffix}' => $suffix,
'{.suffix}' => $suffix ? '.' . $suffix : '',
'{filemd5}' => $md5,
];
$savekey = $savekey ? $savekey : $this->config['saveKey'];
$savekey = str_replace(array_keys($replaceArr), array_values($replaceArr), $savekey);
return $savekey;
}
/**
* 普通文件上传
* @param null $savekey
* @param int $tid
* @return File
* @throws Exception
* @throws \think\exception\DbException
*/
public function upload($savekey = null): File
{
// 判断文件是否为空
if (empty($this->file)) {
throw new Exception('未选择文件或文件超出服务器限制');
}
$this->checkSize();
$this->checkDisabledFile();
$this->checkMimeType();
$savekey = $savekey ? $savekey : $this->getSavekey();
$savekey = '/' . ltrim($savekey, '/');
// 设置上传文件路径
$uploadDir = substr($savekey, 0, strripos($savekey, '/') + 1);
$fileName = substr($savekey, strripos($savekey, '/') + 1);
$destDir = ROOT_PATH . 'public' . str_replace('/', DS, $uploadDir);
$sha1 = $this->file->hash();
if ($this->mergin) {
} else {
$file = $this->file->move($destDir, $fileName);
if (!$file) {
// 捕获上传失败的错误信息
throw new Exception($this->file->getError());
}
}
$this->file = $file;
$params = array(
'filename' => mb_substr(htmlspecialchars(strip_tags($this->fileInfo['name'])), 0, 100),
'tid' => $this->tid,
'filesize' => $this->fileInfo['size'],
'mimetype' => $this->fileInfo['type'],
'uri' => $uploadDir . $file->getSaveName(),
'upload_time' => time(),
'sha1' => $sha1
);
$files = new File();
$files->data(array_filter($params));
$files->save();
return $files;
}
}