更新统一使用 PHPDoc + PSR-19 标准注释

This commit is contained in:
技术老胡
2026-04-21 08:38:59 +08:00
parent dcd58e24ce
commit 9a16a88640
252 changed files with 9218 additions and 659 deletions

View File

@@ -3,57 +3,69 @@
namespace app\service\file\storage;
use app\common\constant\FileConstant;
use app\exception\ResourceNotFoundException;
use app\service\file\StorageConfigService;
use support\Response;
/**
* 文件存储驱动抽象基类。
*
* 提供文件存储驱动公共能力。
*
* @property-read StorageConfigService $storageConfigService 存储配置服务
*/
abstract class AbstractStorageDriver implements StorageDriverInterface
{
/**
* 注入存储配置服务。
*
* @param StorageConfigService $storageConfigService 存储配置服务
*/
public function __construct(
protected StorageConfigService $storageConfigService
) {
}
/**
* 从资产数组中读取指定字段。
*
* @param array<string, mixed> $asset 文件资产数据
* @param string $key 字段名
* @param mixed $default 默认值
* @return mixed 资产字段值
*/
protected function assetValue(array $asset, string $key, mixed $default = null): mixed
{
return $asset[$key] ?? $default;
}
/**
* 解析本地存储文件的绝对路径。
*
* @param array $asset 文件资产数据
* @return string 绝对路径
*/
protected function resolveLocalAbsolutePath(array $asset): string
{
$objectKey = trim((string) $this->assetValue($asset, 'object_key', ''));
if ($objectKey === '') {
return '';
}
$visibility = (int) $this->assetValue($asset, 'visibility', FileConstant::VISIBILITY_PRIVATE);
$candidate = '';
if ($objectKey !== '') {
$candidate = $this->storageConfigService->buildLocalAbsolutePath($visibility, $objectKey);
if ($candidate !== '' && is_file($candidate)) {
return $candidate;
}
}
foreach (['url', 'public_url'] as $field) {
$url = trim((string) $this->assetValue($asset, $field, ''));
if ($url === '') {
continue;
}
$parsedPath = (string) parse_url($url, PHP_URL_PATH);
if ($parsedPath === '') {
continue;
}
$candidate = public_path() . DIRECTORY_SEPARATOR . ltrim($parsedPath, '/');
if (is_file($candidate)) {
return $candidate;
}
}
return $candidate;
return $this->storageConfigService->buildLocalAbsolutePath($visibility, $objectKey);
}
/**
* 构造字符串响应。
*
* @param string $body 响应内容
* @param string $mimeType MIME 类型
* @param int $status HTTP 状态码
* @param array $headers 额外响应头
* @return Response 响应对象
*/
protected function bodyResponse(string $body, string $mimeType = 'application/octet-stream', int $status = 200, array $headers = []): Response
{
$responseHeaders = array_merge([
@@ -63,6 +75,14 @@ abstract class AbstractStorageDriver implements StorageDriverInterface
return response($body, $status, $responseHeaders);
}
/**
* 构造文件下载响应。
*
* @param string $body 响应内容
* @param string $downloadName 下载文件名
* @param string $mimeType MIME 类型
* @return Response 响应对象
*/
protected function downloadBodyResponse(string $body, string $downloadName, string $mimeType = 'application/octet-stream'): Response
{
$response = $this->bodyResponse($body, $mimeType, 200, [
@@ -72,6 +92,14 @@ abstract class AbstractStorageDriver implements StorageDriverInterface
return $response;
}
/**
* 根据本地路径构造预览或下载响应。
*
* @param string $path 本地文件路径
* @param string $downloadName 下载文件名
* @param bool $attachment 是否下载附件
* @return Response 响应对象
*/
protected function responseFromPath(string $path, string $downloadName = '', bool $attachment = false): Response
{
if ($attachment) {
@@ -81,26 +109,46 @@ abstract class AbstractStorageDriver implements StorageDriverInterface
return response()->file($path);
}
/**
* 构造本地文件预览响应。
*
* @param array $asset 文件资产数据
* @return Response 响应对象
* @throws ResourceNotFoundException
*/
protected function localPreviewResponse(array $asset): Response
{
$path = $this->resolveLocalAbsolutePath($asset);
if ($path === '' || !is_file($path)) {
return response('文件不存在', 404);
throw new ResourceNotFoundException('文件不存在');
}
return $this->responseFromPath($path);
}
/**
* 构造本地文件下载响应。
*
* @param array $asset 文件资产数据
* @return Response 响应对象
* @throws ResourceNotFoundException
*/
protected function localDownloadResponse(array $asset): Response
{
$path = $this->resolveLocalAbsolutePath($asset);
if ($path === '' || !is_file($path)) {
return response('文件不存在', 404);
throw new ResourceNotFoundException('文件不存在');
}
return $this->responseFromPath($path, (string) $this->assetValue($asset, 'original_name', basename($path)), true);
}
/**
* 根据文件场景返回目录前缀。
*
* @param int $scene 文件场景
* @return string 目录前缀
*/
protected function scenePrefix(int $scene): string
{
return match ($scene) {

View File

@@ -4,20 +4,36 @@ namespace app\service\file\storage;
use app\common\constant\FileConstant;
use app\exception\BusinessStateException;
use app\exception\ResourceNotFoundException;
use Qcloud\Cos\Client as CosClient;
use support\Response;
use Throwable;
/**
* 腾讯云 COS 文件存储驱动。
*
* 负责对象上传、删除、公开地址生成和对象内容响应。
*/
class CosStorageDriver extends AbstractStorageDriver
{
/**
* 获取 COS 存储引擎标识。
*
* @return int 存储引擎常量
*/
public function engine(): int
{
return FileConstant::STORAGE_TENCENT_COS;
}
/**
* 将本地临时文件上传到 COS。
*
* @param string $sourcePath 待上传文件路径
* @param array $context 上传上下文,包含 object_key、visibility 等信息
* @return array 上传后的资产数据
* @throws BusinessStateException
*/
public function storeFromPath(string $sourcePath, array $context): array
{
if (!is_file($sourcePath)) {
@@ -33,6 +49,7 @@ class CosStorageDriver extends AbstractStorageDriver
$client = $this->client($config);
$objectKey = (string) ($context['object_key'] ?? '');
$visibility = (int) ($context['visibility'] ?? FileConstant::VISIBILITY_PRIVATE);
$client->putObject([
'Bucket' => (string) $config['bucket'],
'Key' => $objectKey,
@@ -40,23 +57,30 @@ class CosStorageDriver extends AbstractStorageDriver
]);
$publicUrl = $this->publicUrl([
'visibility' => $visibility,
'object_key' => $objectKey,
]);
$visibility = (int) ($context['visibility'] ?? FileConstant::VISIBILITY_PRIVATE);
return [
'storage_engine' => $this->engine(),
'object_key' => $objectKey,
'url' => $visibility === FileConstant::VISIBILITY_PUBLIC ? $publicUrl : '',
'public_url' => $publicUrl,
'public_url' => $visibility === FileConstant::VISIBILITY_PUBLIC ? $publicUrl : '',
];
}
/**
* 删除 COS 对象。
*
* @param array $asset 文件资产数据
* @return bool 是否删除成功
* @throws BusinessStateException
*/
public function delete(array $asset): bool
{
$config = $this->storageConfigService->cosConfig();
if (trim((string) ($config['bucket'] ?? '')) === '') {
return false;
throw new BusinessStateException('腾讯云 COS 存储配置未完整');
}
$objectKey = (string) ($asset['object_key'] ?? '');
@@ -73,23 +97,41 @@ class CosStorageDriver extends AbstractStorageDriver
return true;
}
/**
* 构造 COS 文件预览响应。
*
* @param array $asset 文件资产数据
* @return Response 响应对象
*/
public function previewResponse(array $asset): Response
{
$url = $this->publicUrl($asset);
if ($url !== '') {
return redirect($url);
}
return $this->responseFromObject($asset, false);
}
/**
* 构造 COS 文件下载响应。
*
* @param array $asset 文件资产数据
* @return Response 响应对象
*/
public function downloadResponse(array $asset): Response
{
return $this->responseFromObject($asset, true);
}
/**
* 获取 COS 公开访问地址。
*
* @param array $asset 文件资产数据
* @return string 公共 URL
*/
public function publicUrl(array $asset): string
{
$visibility = (int) ($asset['visibility'] ?? FileConstant::VISIBILITY_PRIVATE);
if ($visibility !== FileConstant::VISIBILITY_PUBLIC) {
return '';
}
$publicUrl = trim((string) ($asset['url'] ?? $asset['public_url'] ?? ''));
if ($publicUrl !== '') {
return $publicUrl;
@@ -115,11 +157,17 @@ class CosStorageDriver extends AbstractStorageDriver
return 'https://' . $bucket . '.cos.' . $region . '.myqcloud.com/' . ltrim($objectKey, '/');
}
/**
* 获取 COS 临时访问地址。
*
* @param array $asset 文件资产数据
* @return string 临时 URL
*/
public function temporaryUrl(array $asset): string
{
$config = $this->storageConfigService->cosConfig();
if (trim((string) ($config['bucket'] ?? '')) === '' || trim((string) ($config['region'] ?? '')) === '') {
return $this->publicUrl($asset);
return '';
}
try {
@@ -134,10 +182,16 @@ class CosStorageDriver extends AbstractStorageDriver
$objectKey
);
} catch (Throwable) {
return $this->publicUrl($asset);
return '';
}
}
/**
* 创建 COS 客户端。
*
* @param array $config 存储配置
* @return CosClient COS 客户端
*/
private function client(array $config): CosClient
{
return new CosClient([
@@ -149,13 +203,21 @@ class CosStorageDriver extends AbstractStorageDriver
]);
}
/**
* 根据 COS 对象内容构造预览或下载响应。
*
* @param array $asset 文件资产数据
* @param bool $attachment 是否下载附件
* @return Response 响应对象
* @throws ResourceNotFoundException
*/
private function responseFromObject(array $asset, bool $attachment): Response
{
$config = $this->storageConfigService->cosConfig();
$bucket = trim((string) ($config['bucket'] ?? ''));
$objectKey = (string) ($asset['object_key'] ?? '');
if ($bucket === '' || $objectKey === '') {
return response('文件不存在', 404);
throw new ResourceNotFoundException('文件不存在');
}
try {
@@ -171,7 +233,7 @@ class CosStorageDriver extends AbstractStorageDriver
} elseif (is_object($result) && method_exists($result, '__toString')) {
$body = (string) $result;
} elseif (is_array($result)) {
$body = (string) ($result['Body'] ?? $result['body'] ?? '');
$body = (string) ($result['Body'] ?? $result['body'] ?? '');
}
$mimeType = (string) ($asset['mime_type'] ?? 'application/octet-stream');
@@ -182,7 +244,7 @@ class CosStorageDriver extends AbstractStorageDriver
return $this->bodyResponse($body, $mimeType);
} catch (Throwable) {
return response('文件不存在', 404);
throw new ResourceNotFoundException('文件不存在');
}
}
}

View File

@@ -8,14 +8,29 @@ use support\Response;
/**
* 本地文件存储驱动。
*
* 负责本地文件存储和响应构造。
*/
class LocalStorageDriver extends AbstractStorageDriver
{
/**
* 获取本地存储引擎标识。
*
* @return int 存储引擎常量
*/
public function engine(): int
{
return FileConstant::STORAGE_LOCAL;
}
/**
* 将临时文件写入本地存储目录。
*
* @param string $sourcePath 待上传文件路径
* @param array $context 上传上下文,包含 object_key、visibility、public_url 等信息
* @return array 上传后的资产数据
* @throws BusinessStateException
*/
public function storeFromPath(string $sourcePath, array $context): array
{
if (!is_file($sourcePath)) {
@@ -51,6 +66,13 @@ class LocalStorageDriver extends AbstractStorageDriver
];
}
/**
* 删除本地文件。
*
* @param array $asset 文件资产数据
* @return bool 是否删除成功
* @throws BusinessStateException
*/
public function delete(array $asset): bool
{
$path = $this->resolveLocalAbsolutePath($asset);
@@ -58,26 +80,43 @@ class LocalStorageDriver extends AbstractStorageDriver
return true;
}
return @unlink($path);
if (@unlink($path)) {
return true;
}
throw new BusinessStateException('本地文件删除失败');
}
/**
* 构造本地文件预览响应。
*
* @param array $asset 文件资产数据
* @return Response 响应对象
*/
public function previewResponse(array $asset): Response
{
return $this->localPreviewResponse($asset);
}
/**
* 构造本地文件下载响应。
*
* @param array $asset 文件资产数据
* @return Response 响应对象
*/
public function downloadResponse(array $asset): Response
{
return $this->localDownloadResponse($asset);
}
/**
* 获取本地公开访问地址。
*
* @param array $asset 文件资产数据
* @return string 公共 URL
*/
public function publicUrl(array $asset): string
{
$url = trim((string) ($asset['url'] ?? $asset['public_url'] ?? ''));
if ($url !== '') {
return $url;
}
$visibility = (int) ($asset['visibility'] ?? FileConstant::VISIBILITY_PRIVATE);
if ($visibility !== FileConstant::VISIBILITY_PUBLIC) {
return '';
@@ -91,13 +130,14 @@ class LocalStorageDriver extends AbstractStorageDriver
return $this->storageConfigService->buildLocalPublicUrl($objectKey);
}
/**
* 获取本地临时访问地址。
*
* @param array $asset 文件资产数据
* @return string 临时 URL
*/
public function temporaryUrl(array $asset): string
{
$url = trim((string) ($asset['url'] ?? $asset['public_url'] ?? ''));
if ($url !== '') {
return $url;
}
$visibility = (int) ($asset['visibility'] ?? FileConstant::VISIBILITY_PRIVATE);
if ($visibility === FileConstant::VISIBILITY_PUBLIC) {
return $this->publicUrl($asset);
@@ -108,6 +148,12 @@ class LocalStorageDriver extends AbstractStorageDriver
return $id > 0 ? '/adminapi/file-asset/' . $id . '/preview' : '';
}
/**
* 确保目标目录存在。
*
* @param string $directory 目录路径
* @throws BusinessStateException
*/
private function ensureDirectory(string $directory): void
{
if (is_dir($directory)) {

View File

@@ -4,20 +4,36 @@ namespace app\service\file\storage;
use app\common\constant\FileConstant;
use app\exception\BusinessStateException;
use app\exception\ResourceNotFoundException;
use AlibabaCloud\Oss\V2 as Oss;
use support\Response;
use Throwable;
/**
* 阿里云 OSS 文件存储驱动。
*
* 负责对象上传、删除、公开地址生成和预签名访问。
*/
class OssStorageDriver extends AbstractStorageDriver
{
/**
* 获取 OSS 存储引擎标识。
*
* @return int 存储引擎常量
*/
public function engine(): int
{
return FileConstant::STORAGE_ALIYUN_OSS;
}
/**
* 将本地临时文件上传到 OSS。
*
* @param string $sourcePath 待上传文件路径
* @param array $context 上传上下文,包含 object_key、visibility 等信息
* @return array 上传后的资产数据
* @throws BusinessStateException
*/
public function storeFromPath(string $sourcePath, array $context): array
{
if (!is_file($sourcePath)) {
@@ -33,6 +49,9 @@ class OssStorageDriver extends AbstractStorageDriver
$client = $this->client($config);
$objectKey = (string) ($context['object_key'] ?? '');
$visibility = (int) ($context['visibility'] ?? FileConstant::VISIBILITY_PRIVATE);
/** @var Oss\Models\PutObjectRequest $request */
$request = new Oss\Models\PutObjectRequest(
bucket: (string) $config['bucket'],
key: $objectKey
@@ -42,23 +61,30 @@ class OssStorageDriver extends AbstractStorageDriver
$client->putObject($request);
$publicUrl = $this->publicUrl([
'visibility' => $visibility,
'object_key' => $objectKey,
]);
$visibility = (int) ($context['visibility'] ?? FileConstant::VISIBILITY_PRIVATE);
return [
'storage_engine' => $this->engine(),
'object_key' => $objectKey,
'url' => $visibility === FileConstant::VISIBILITY_PUBLIC ? $publicUrl : '',
'public_url' => $publicUrl,
'public_url' => $visibility === FileConstant::VISIBILITY_PUBLIC ? $publicUrl : '',
];
}
/**
* 删除 OSS 对象。
*
* @param array $asset 文件资产数据
* @return bool 是否删除成功
* @throws BusinessStateException
*/
public function delete(array $asset): bool
{
$config = $this->storageConfigService->ossConfig();
if (trim((string) ($config['bucket'] ?? '')) === '') {
return false;
throw new BusinessStateException('阿里云 OSS 存储配置未完整');
}
$objectKey = (string) ($asset['object_key'] ?? '');
@@ -67,6 +93,8 @@ class OssStorageDriver extends AbstractStorageDriver
}
$client = $this->client($config);
/** @var Oss\Models\DeleteObjectRequest $request */
$request = new Oss\Models\DeleteObjectRequest(
bucket: (string) $config['bucket'],
key: $objectKey
@@ -76,23 +104,41 @@ class OssStorageDriver extends AbstractStorageDriver
return true;
}
/**
* 构造 OSS 文件预览响应。
*
* @param array $asset 文件资产数据
* @return Response 响应对象
*/
public function previewResponse(array $asset): Response
{
$url = $this->publicUrl($asset);
if ($url !== '') {
return redirect($url);
}
return $this->responseFromObject($asset, false);
}
/**
* 构造 OSS 文件下载响应。
*
* @param array $asset 文件资产数据
* @return Response 响应对象
*/
public function downloadResponse(array $asset): Response
{
return $this->responseFromObject($asset, true);
}
/**
* 获取 OSS 公开访问地址。
*
* @param array $asset 文件资产数据
* @return string 公共 URL
*/
public function publicUrl(array $asset): string
{
$visibility = (int) ($asset['visibility'] ?? FileConstant::VISIBILITY_PRIVATE);
if ($visibility !== FileConstant::VISIBILITY_PUBLIC) {
return '';
}
$publicUrl = trim((string) ($asset['url'] ?? $asset['public_url'] ?? ''));
if ($publicUrl !== '') {
return $publicUrl;
@@ -120,11 +166,17 @@ class OssStorageDriver extends AbstractStorageDriver
return 'https://' . $bucket . '.' . ltrim($endpoint, '/') . '/' . ltrim($objectKey, '/');
}
/**
* 获取 OSS 预签名访问地址。
*
* @param array $asset 文件资产数据
* @return string 临时 URL
*/
public function temporaryUrl(array $asset): string
{
$config = $this->storageConfigService->ossConfig();
if (trim((string) ($config['bucket'] ?? '')) === '' || trim((string) ($config['region'] ?? '')) === '') {
return $this->publicUrl($asset);
return '';
}
try {
@@ -134,6 +186,7 @@ class OssStorageDriver extends AbstractStorageDriver
return '';
}
/** @var Oss\Models\GetObjectRequest $request */
$request = new Oss\Models\GetObjectRequest(
bucket: (string) $config['bucket'],
key: $objectKey
@@ -142,12 +195,19 @@ class OssStorageDriver extends AbstractStorageDriver
return (string) ($result->url ?? '');
} catch (Throwable) {
return $this->publicUrl($asset);
return '';
}
}
/**
* 创建 OSS 客户端。
*
* @param array $config 存储配置
* @return Oss\Client OSS 客户端
*/
private function client(array $config): Oss\Client
{
/** @var Oss\Credentials\StaticCredentialsProvider $provider */
$provider = new Oss\Credentials\StaticCredentialsProvider(
accessKeyId: (string) $config['access_key_id'],
accessKeySecret: (string) $config['access_key_secret']
@@ -165,17 +225,26 @@ class OssStorageDriver extends AbstractStorageDriver
return new Oss\Client($cfg);
}
/**
* 根据 OSS 对象内容构造预览或下载响应。
*
* @param array $asset 文件资产数据
* @param bool $attachment 是否下载附件
* @return Response 响应对象
* @throws ResourceNotFoundException
*/
private function responseFromObject(array $asset, bool $attachment): Response
{
$config = $this->storageConfigService->ossConfig();
$bucket = trim((string) ($config['bucket'] ?? ''));
$objectKey = (string) ($asset['object_key'] ?? '');
if ($bucket === '' || $objectKey === '') {
return response('文件不存在', 404);
throw new ResourceNotFoundException('文件不存在');
}
try {
$client = $this->client($config);
/** @var Oss\Models\GetObjectRequest $request */
$request = new Oss\Models\GetObjectRequest(
bucket: $bucket,
key: $objectKey
@@ -190,7 +259,7 @@ class OssStorageDriver extends AbstractStorageDriver
return $this->bodyResponse($body, $mimeType);
} catch (Throwable) {
return response('文件不存在', 404);
throw new ResourceNotFoundException('文件不存在');
}
}
}

View File

@@ -4,48 +4,95 @@ namespace app\service\file\storage;
use app\common\constant\FileConstant;
use app\exception\BusinessStateException;
use app\exception\ResourceNotFoundException;
use support\Response;
/**
* 远程引用驱动。
* 远程引用文件存储驱动。
*
* 仅保存原始远程 URL不做本地落盘或对象存储复制。
*/
class RemoteUrlStorageDriver extends AbstractStorageDriver
{
/**
* 获取远程引用引擎标识。
*
* @return int 存储引擎常量
*/
public function engine(): int
{
return FileConstant::STORAGE_REMOTE_URL;
}
/**
* 远程引用模式不支持直接上传。
*
* @param string $sourcePath 待上传文件路径
* @param array $context 上传上下文
* @return array 上传后的资产数据
* @throws BusinessStateException
*/
public function storeFromPath(string $sourcePath, array $context): array
{
throw new BusinessStateException('远程引用模式不支持直接上传,请先下载后再入库');
}
/**
* 远程引用模式不需要真正删除对象。
*
* @param array $asset 文件资产数据
* @return bool 是否删除成功
*/
public function delete(array $asset): bool
{
return true;
}
/**
* 直接跳转到源站地址进行预览。
*
* @param array $asset 文件资产数据
* @return Response 响应对象
* @throws ResourceNotFoundException
*/
public function previewResponse(array $asset): Response
{
$url = (string) ($asset['source_url'] ?? $asset['url'] ?? '');
if ($url === '') {
return response('文件不存在', 404);
throw new ResourceNotFoundException('文件不存在');
}
return redirect($url);
}
/**
* 远程引用文件的下载行为与预览保持一致。
*
* @param array $asset 文件资产数据
* @return Response 响应对象
*/
public function downloadResponse(array $asset): Response
{
return $this->previewResponse($asset);
}
/**
* 获取原始远程地址。
*
* @param array $asset 文件资产数据
* @return string 远程 URL
*/
public function publicUrl(array $asset): string
{
return (string) ($asset['source_url'] ?? $asset['url'] ?? '');
}
/**
* 获取原始远程地址。
*
* @param array $asset 文件资产数据
* @return string 远程 URL
*/
public function temporaryUrl(array $asset): string
{
return (string) ($asset['source_url'] ?? $asset['url'] ?? '');

View File

@@ -6,20 +6,64 @@ use support\Response;
/**
* 文件存储驱动接口。
*
* 统一定义文件存储驱动能力。
*/
interface StorageDriverInterface
{
/**
* 获取存储引擎标识。
*
* @return int 存储引擎常量
*/
public function engine(): int;
/**
* 将本地临时文件写入存储后端。
*
* @param string $sourcePath 待上传的本地临时文件路径
* @param array $context 上传上下文,通常包含 object_key、visibility 等信息
* @return array 上传后的资产数据
*/
public function storeFromPath(string $sourcePath, array $context): array;
/**
* 删除指定文件资产。
*
* @param array $asset 文件资产数据
* @return bool 是否删除成功
*/
public function delete(array $asset): bool;
/**
* 构造文件预览响应。
*
* @param array $asset 文件资产数据
* @return Response 响应对象
*/
public function previewResponse(array $asset): Response;
/**
* 构造文件下载响应。
*
* @param array $asset 文件资产数据
* @return Response 响应对象
*/
public function downloadResponse(array $asset): Response;
/**
* 获取公开访问地址。
*
* @param array $asset 文件资产数据
* @return string 公开 URL
*/
public function publicUrl(array $asset): string;
/**
* 获取临时访问地址。
*
* @param array $asset 文件资产数据
* @return string 临时 URL
*/
public function temporaryUrl(array $asset): string;
}

View File

@@ -8,9 +8,27 @@ use support\Response;
/**
* 文件存储驱动管理器。
*
* 负责根据存储引擎分发文件操作。
*
* @property StorageConfigService $storageConfigService 存储配置服务
* @property LocalStorageDriver $localStorageDriver 本地存储驱动
* @property OssStorageDriver $ossStorageDriver oss存储驱动
* @property CosStorageDriver $cosStorageDriver cos存储驱动
* @property RemoteUrlStorageDriver $remoteUrlStorageDriver remoteUrl存储驱动
*/
class StorageManager
{
/**
* 构造方法。
*
* @param StorageConfigService $storageConfigService 存储配置服务
* @param LocalStorageDriver $localStorageDriver 本地存储驱动
* @param OssStorageDriver $ossStorageDriver oss存储驱动
* @param CosStorageDriver $cosStorageDriver cos存储驱动
* @param RemoteUrlStorageDriver $remoteUrlStorageDriver remoteUrl存储驱动
* @return void
*/
public function __construct(
protected StorageConfigService $storageConfigService,
protected LocalStorageDriver $localStorageDriver,
@@ -20,6 +38,18 @@ class StorageManager
) {
}
/**
* 构建存储上下文。
*
* @param string $sourcePath 源文件路径
* @param string $originalName 原始文件名
* @param int|null $scene 场景
* @param int|null $visibility 可见性
* @param int|null $engine 存储引擎
* @param string|null $sourceUrl 源地址
* @param string $sourceType 来源类型
* @return array 上下文数据
*/
public function buildContext(
string $sourcePath,
string $originalName,
@@ -58,6 +88,18 @@ class StorageManager
];
}
/**
* 从文件路径保存文件。
*
* @param string $sourcePath 源文件路径
* @param string $originalName 原始文件名
* @param int|null $scene 场景
* @param int|null $visibility 可见性
* @param int|null $engine 存储引擎
* @param string|null $sourceUrl 源地址
* @param string $sourceType 来源类型
* @return array 保存结果
*/
public function storeFromPath(
string $sourcePath,
string $originalName,
@@ -73,36 +115,72 @@ class StorageManager
return array_merge($context, $driver->storeFromPath($sourcePath, $context));
}
/**
* 删除存储对象。
*
* @param array $asset 文件记录
* @return bool 是否删除成功
*/
public function delete(array $asset): bool
{
return $this->resolveDriver((int) ($asset['storage_engine'] ?? FileConstant::STORAGE_LOCAL))
->delete($asset);
}
/**
* 获取预览响应。
*
* @param array $asset 文件记录
* @return Response 响应对象
*/
public function previewResponse(array $asset): Response
{
return $this->resolveDriver((int) ($asset['storage_engine'] ?? FileConstant::STORAGE_LOCAL))
->previewResponse($asset);
}
/**
* 获取下载响应。
*
* @param array $asset 文件记录
* @return Response 响应对象
*/
public function downloadResponse(array $asset): Response
{
return $this->resolveDriver((int) ($asset['storage_engine'] ?? FileConstant::STORAGE_LOCAL))
->downloadResponse($asset);
}
/**
* 获取公开访问 URL。
*
* @param array $asset 文件记录
* @return string 访问 URL
*/
public function publicUrl(array $asset): string
{
return $this->resolveDriver((int) ($asset['storage_engine'] ?? FileConstant::STORAGE_LOCAL))
->publicUrl($asset);
}
/**
* 获取临时访问 URL。
*
* @param array $asset 文件记录
* @return string 访问 URL
*/
public function temporaryUrl(array $asset): string
{
return $this->resolveDriver((int) ($asset['storage_engine'] ?? FileConstant::STORAGE_LOCAL))
->temporaryUrl($asset);
}
/**
* 解析对应的存储驱动。
*
* @param int $engine 存储引擎
* @return StorageDriverInterface 存储驱动
*/
public function resolveDriver(int $engine): StorageDriverInterface
{
return match ($engine) {
@@ -114,6 +192,14 @@ class StorageManager
};
}
/**
* 按存储引擎构建公开访问 URL。
*
* @param int $engine 存储引擎
* @param int $visibility 可见性
* @param string $objectKey 对象键
* @return string 访问 URL
*/
private function buildPublicUrlByEngine(int $engine, int $visibility, string $objectKey): string
{
if ($engine === FileConstant::STORAGE_LOCAL && $visibility === FileConstant::VISIBILITY_PUBLIC) {
@@ -123,6 +209,13 @@ class StorageManager
return '';
}
/**
* 估算 MIME 类型。
*
* @param string $sourcePath 源文件路径
* @param string $originalName 原始文件名
* @return string MIME 类型
*/
private function guessMimeType(string $sourcePath, string $originalName): string
{
$mimeType = '';
@@ -156,3 +249,5 @@ class StorageManager
};
}
}