tapi/app/service/file/FilesystemService.php
u2nyakim e2c86c0c6d up.
2025-08-27 10:05:35 +08:00

244 lines
8.9 KiB
PHP

<?php
namespace app\service\file;
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\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 array
*/
public function upload(UploadedFile $file, int $directory_id, int $user_id, array $vars = []): array
{
$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 = $this->app->config->get('filesystem.model');
if (empty($filesystem_model)) {
throw new FileException('File记录模型未定义');
}
/**
* @var SysFileRule $fileRule
*/
$fileRule = SysFileRule::find($rule_id);
if (empty($fileRule)) {
throw new ValidateException('Rule: 上传规则不存在');
}
if ($fileRule['is_close']) {
throw new ValidateException('Rule: 已关闭上传权限');
}
/*
* 根据规则校验上传的文件是否符合要求
*/
$this->checkFileRule($fileRule, $file);
$putDisk = $fileRule->disk;
$putUrl = $fileRule->url;
$putPath = $fileRule->path_rule;
$nameRule = $fileRule->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);
// dump(['#1', $putPath, $nameRule]);
// dump(['#2', $filePath, $fileName]);
$filesystemSrv = Filesystem::disk($putDisk);
$uploadPath = $filesystemSrv->putFile($filePath, $file, function () use ($fileName) {
return $fileName;
});
if ($uploadPath === false) {
throw new FileException('上传失败');
}
$diskConfig = Filesystem::getDiskConfig($putDisk);
$url = $diskConfig['url'] ?? '';
if ($url && Validate::url($url) === false) {
$url = trim($url, '/');
$uploadPath = "$url/$uploadPath";
$url = "";
}
$filesystem_apiUrl = $this->app->config->get('filesystem.api_url');
/*
* URL取值优先级为
* #1 取fileRule中的设置 -> #2 取config中的设置 -> #3 取api_url的配置 #4 取当前域名的配置
* 注意, fileRule中的permissions配置会把#3,#4做为优先项
*/
$diskUrl = $putUrl ?: ($url ?: $filesystem_apiUrl ?: Request::domain());
$uploadSavePath = pathinfo($uploadPath, PATHINFO_DIRNAME);
$fileSaveName = pathinfo($uploadPath, PATHINFO_BASENAME);
// dump([$diskUrl, $uploadPath]);
// dump(['#4', $uploadSavePath, $fileSaveName]);
$sysFileRecord = $this->app->invokeClass($filesystem_model);
$sysFileRecord->save([
'name' => $fileSaveName,
'origin_name' => $file->getOriginalName(),
'alias_name' => '',
'path' => $uploadSavePath,
'length' => $file->getSize(),
'extension' => $file->getOriginalExtension(),
'content_type' => mime_content_type($file->getRealPath()) ?: '',
'md5' => $file->md5(),
'sha1' => $file->sha1(),
'disk' => $putDisk,
'uid' => $user_id,
'create_user_id' => $user_id,
'context_id' => $this->app->request->contextId,
'gid' => 0,
'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,
'uuid' => guid()
]);
$sysFileRecordData = $sysFileRecord->toArray();
$fileRuleData = $fileRule->toArray();
if ($user_id > 0) {
(new SysUserFile)->save([
'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,
// 磁盘
'disk' => $putDisk,
// 存储规则
'rule_id' => $rule_id,
'uuid' => $sysFileRecord->uuid,
'url' => "$diskUrl/{$uploadPath}",
'is_anonymous' => 0,
]);
}
// 是否需要安全访问
if ($fileRule->permissions) {
$uploadPath = "i/" . $sysFileRecord->uuid;
$diskUrl = $filesystem_apiUrl ?: Request::domain();
}
$results = [
// 文件对外显示URL
'url' => "$diskUrl/{$uploadPath}",
// 文件对外显示存储路径
'path' => $uploadPath,
// 文件唯一标识符
'uuid' => "{$rule_id}@" . $sysFileRecord->uuid,
// 文件上传时间戳
'time' => time(),
];
// 触发文件上传事件
Event::trigger('upload', ['results' => $results, 'file' => $file, 'rule' => $fileRuleData, 'model' => $sysFileRecordData, 'directory_id' => $directory_id]);
// 触发磁盘上传事件
Event::trigger("upload.$putDisk", ['results' => $results, 'file' => $file, 'rule' => $fileRuleData, 'model' => $sysFileRecordData, 'directory_id' => $directory_id]);
// 触发磁盘某个规则的上传事件
Event::trigger("upload.rule_$rule_id", ['results' => $results, 'file' => $file, 'rule' => $fileRuleData, 'model' => $sysFileRecordData, 'directory_id' => $directory_id]);
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]);
}
}
}