266 lines
9.0 KiB
PHP
266 lines
9.0 KiB
PHP
<?php
|
|
|
|
namespace app\service\file;
|
|
|
|
use app\entity\file\UploadResult;
|
|
use app\entity\SysFileRecord;
|
|
use app\entity\SysFileRule;
|
|
use app\entity\SysUserFile;
|
|
use think\facade\Event;
|
|
use think\facade\Request;
|
|
use think\App;
|
|
use think\exception\FileException;
|
|
use think\exception\ValidateException;
|
|
use think\facade\Filesystem;
|
|
use think\facade\Validate;
|
|
use think\file\UploadedFile;
|
|
use think\filesystem\Driver;
|
|
use think\helper\Str;
|
|
|
|
class FilesystemService
|
|
{
|
|
protected App $app;
|
|
|
|
public function __construct(App $app)
|
|
{
|
|
$this->app = $app;
|
|
}
|
|
|
|
/**
|
|
* @param UploadedFile $file 上传的文件
|
|
* @param int $directory_id 存储的目录位置
|
|
* @param int $user_id 用户ID
|
|
* @param array $vars
|
|
* @return UploadResult
|
|
*/
|
|
public function upload(UploadedFile $file, int $directory_id, int $user_id, array $vars = []): UploadResult
|
|
{
|
|
$rule_id = 1;
|
|
if ($directory_id > 0) {
|
|
// 上传到某个目录下
|
|
$directory = SysUserFile::where('is_directory', 1)->find($directory_id);
|
|
if (empty($directory)) {
|
|
throw new ValidateException('Dir: 目录不存在');
|
|
}
|
|
}
|
|
/*
|
|
* 文件记录实体
|
|
* filesystem.model
|
|
*/
|
|
$filesystem_model = $this->app->config->get('filesystem.model');
|
|
if (empty($filesystem_model)) {
|
|
throw new FileException('File记录模型未定义');
|
|
}
|
|
/*
|
|
* 文件预览接口(用于安全访问模式)
|
|
* filesystem.api_url
|
|
*/
|
|
$filesystem_apiUrl = $this->app->config->get('filesystem.api_url');
|
|
if (empty($filesystem_apiUrl)) {
|
|
throw new FileException('请正确配置filesystem.api_url');
|
|
}
|
|
if ($filesystem_apiUrl == "#") {
|
|
$filesystem_apiUrl = Request::domain();
|
|
} else if (!Validate::url($filesystem_apiUrl)) {
|
|
throw new FileException('请正确配置filesystem.api_url');
|
|
}
|
|
|
|
/**
|
|
* @var SysFileRule|\app\model\SysFileRule $rule
|
|
*/
|
|
$rule = SysFileRule::find($rule_id);
|
|
if (empty($rule)) {
|
|
throw new ValidateException('Rule: 上传规则不存在');
|
|
}
|
|
if ($rule->is_close) {
|
|
throw new ValidateException('Rule: 已关闭上传权限');
|
|
}
|
|
/*
|
|
* 根据规则校验上传的文件是否符合要求
|
|
*/
|
|
$this->checkFileRule($rule, $file);
|
|
|
|
|
|
$putDisk = $rule->disk;
|
|
$putUrl = $rule->url ? $rule->url : $filesystem_apiUrl;
|
|
$putPath = $rule->path_rule;
|
|
$nameRule = $rule->name_rule;
|
|
|
|
|
|
$nameRule = $this->replaceName($nameRule, $file, $vars + ['uid' => $user_id]);
|
|
$putPath = $this->replaceName($putPath, $file, $vars + ['uid' => $user_id]);
|
|
|
|
$savePath = ($putPath ? trim($putPath, '/') : '') . '/' . $nameRule;
|
|
|
|
$fileName = pathinfo($savePath, PATHINFO_FILENAME);
|
|
$filePath = pathinfo($savePath, PATHINFO_DIRNAME);
|
|
|
|
$disk = Filesystem::disk('public');
|
|
$uploadPath = $this->uploadFile($disk, $filePath, $file, $fileName);
|
|
if ($uploadPath === false) {
|
|
throw new FileException('上传失败');
|
|
}
|
|
$uploadPath = $disk->url($uploadPath);
|
|
|
|
$fileUrl = $putUrl . '/' . ltrim($uploadPath, '/');
|
|
|
|
$uploadSavePath = pathinfo($uploadPath, PATHINFO_DIRNAME);
|
|
$fileSaveName = pathinfo($uploadPath, PATHINFO_BASENAME);
|
|
// 系统文件上传日志记录
|
|
$sysFileRecord = $this->app->invokeClass($filesystem_model);
|
|
$sysFileRecord->save([
|
|
'rule_id' => $rule->id,
|
|
'name' => $fileSaveName,
|
|
'path' => $uploadSavePath,
|
|
'length' => $file->getSize(),
|
|
'extension' => $file->getOriginalExtension(),
|
|
'content_type' => mime_content_type($file->getRealPath()) ?: '',
|
|
'md5' => $file->md5(),
|
|
'disk' => $putDisk,
|
|
'create_user_id' => $user_id,
|
|
'context_id' => $this->app->request->contextId,
|
|
'create_date' => date('Y-m-d'),
|
|
'create_time' => date('Y-m-d H:i:s'),
|
|
'update_time' => date('Y-m-d H:i:s'),
|
|
'delete_time' => null,
|
|
]);
|
|
|
|
|
|
// 是否需要安全访问
|
|
if ($rule->permissions) {
|
|
$fileId = hashids(12)->encode($sysFileRecord->id);
|
|
$uploadPath = "i/" . $fileId;
|
|
$fileUrl = $putUrl . '/' . ltrim($uploadPath, '/');
|
|
}
|
|
|
|
(new SysUserFile)->save([
|
|
'rule_id' => $rule_id,
|
|
'disk' => $putDisk,
|
|
'user_id' => $user_id,
|
|
'name' => $file->getOriginalName(),
|
|
'is_directory' => 0,
|
|
'parent_id' => $directory_id,
|
|
'path' => $uploadPath,
|
|
'length' => $file->getSize(),
|
|
'content_type' => $file->getOriginalMime(),
|
|
'deleted' => 0,
|
|
'url' => $fileUrl,
|
|
'is_anonymous' => 0,
|
|
]);
|
|
|
|
/*
|
|
* 上传结果封装
|
|
*/
|
|
$results = new UploadResult([
|
|
// 文件对外显示URL
|
|
'url' => $fileUrl,
|
|
// 文件对外显示存储路径
|
|
'path' => $uploadPath,
|
|
// 文件上传时间戳
|
|
'time' => time(),
|
|
]);
|
|
|
|
/*
|
|
* 触发上传完成事件
|
|
*/
|
|
$this->triggerUploaded($results, $putDisk, $file, $rule, $sysFileRecord);
|
|
|
|
return $results;
|
|
}
|
|
|
|
|
|
private function replaceName(string $pathname, UploadedFile $file, $vars = []): string
|
|
{
|
|
$array = [
|
|
'{Y}' => date('Y'),
|
|
'{y}' => date('y'),
|
|
'{m}' => date('m'),
|
|
'{d}' => date('d'),
|
|
'{H}' => date('H'),
|
|
'{i}' => date('i'),
|
|
'{s}' => date('s'),
|
|
'{G}' => date('G'),
|
|
'{Ym}' => date('Ym'),
|
|
'{Ymd}' => date('Ymd'),
|
|
'{YmdH}' => date('YmdH'),
|
|
'{timestamp}' => time(),
|
|
'{uniqid}' => uniqid(),
|
|
'{md5}' => md5(microtime() . Str::random()),
|
|
'{md5-16}' => substr(md5(microtime() . Str::random()), 0, 16),
|
|
'{str-random-16}' => Str::random(16),
|
|
'{str-random-10}' => Str::random(10),
|
|
'{str-random-6}' => Str::random(6),
|
|
'{fmd5}' => $file->md5(),
|
|
'{fsha1}' => $file->sha1(),
|
|
'{fname}' => $file->getOriginalName(),
|
|
'{uid}' => $vars['uid'] ?? 'UID',
|
|
];
|
|
// 系统内部的key不允许被覆盖
|
|
$useKeys = array_keys($array);
|
|
foreach ($vars as $key => $value) {
|
|
if (in_array($key, $useKeys)) {
|
|
continue;
|
|
}
|
|
$array["{{$key}}"] = $value;
|
|
}
|
|
return str_replace(array_keys($array), array_values($array), $pathname);
|
|
}
|
|
|
|
private function checkFileRule(SysFileRule|\app\model\SysFileRule $fileRule, UploadedFile $file): void
|
|
{
|
|
/*
|
|
* 验证文件信息
|
|
*/
|
|
if ($fileRule->rule_file_size_limit_min > 0) {
|
|
if ($file->getSize() < $fileRule->rule_file_size_limit_min * 1024) {
|
|
throw new ValidateException('上传限制:文件太小了');
|
|
}
|
|
}
|
|
$validateRule = [];
|
|
if ($fileRule->rule_file_size_limit_max > 0) {
|
|
$validateRule[] = 'fileSize:' . ($fileRule->rule_file_size_limit_max * 1024);
|
|
}
|
|
if ($fileRule->rule_file_ext !== '') {
|
|
$validateRule[] = "fileExt:$fileRule->rule_file_ext";
|
|
}
|
|
if ($fileRule->rule_file_mime !== '') {
|
|
$validateRule[] = "fileMime:$fileRule->rule_file_mime";
|
|
}
|
|
if ($fileRule->rule_image !== '') {
|
|
$validateRule[] = "image:$fileRule->rule_image";
|
|
}
|
|
if ($validateRule) {
|
|
validate(['file' => implode('|', $validateRule)])
|
|
->check(['file' => $file]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 触发上传完成事件
|
|
* @param UploadResult $results 上传结果
|
|
* @param string $putDisk 存储磁盘
|
|
* @param UploadedFile $file 上传的文件
|
|
* @param SysFileRule $rule 上传规则
|
|
* @param SysFileRecord $sysFileRecord 文件上传记录
|
|
* @return void
|
|
*/
|
|
private function triggerUploaded(UploadResult $results, string $putDisk, UploadedFile $file, SysFileRule $rule, SysFileRecord $sysFileRecord): void
|
|
{
|
|
$eventData = ['results' => $results, 'file' => $file, 'rule' => $rule, 'model' => $sysFileRecord];
|
|
// 触发文件上传事件
|
|
Event::trigger('uploaded', $eventData);
|
|
// 触发磁盘上传事件
|
|
Event::trigger("uploaded.$putDisk", $eventData);
|
|
// 触发磁盘某个规则的上传事件
|
|
if (!empty($rule->id)) {
|
|
Event::trigger("uploaded.rule_{$rule->id}", $eventData);
|
|
}
|
|
}
|
|
|
|
private function uploadFile(Driver $driver, string $filePath, UploadedFile $file, string $fileName): bool|string
|
|
{
|
|
return $driver->putFile($filePath, $file, function () use ($fileName) {
|
|
return $fileName;
|
|
});
|
|
}
|
|
} |