diff --git a/docs/zh/backend/base/upload.md b/docs/zh/backend/base/upload.md new file mode 100644 index 0000000..e5d07c9 --- /dev/null +++ b/docs/zh/backend/base/upload.md @@ -0,0 +1,323 @@ +# 文件上传 + +## 后端上传 + +::: tip + +文件上传由 MineAdmin 接入 [mineadmin/upload](https://github.com/mineadmin/upload) 组件建设而成 + +::: + +MineAdmin 提供了一个默认的服务端文件上传逻辑。接口 `/admin/attachment/upload` +并且在上传成功后接入了资源管理器。 + +::: code-group + +```php [ControllerUpload] +#[Post( + path: '/admin/attachment/upload', + operationId: 'UploadAttachment', + summary: '上传附件', + security: [['Bearer' => [], 'ApiKey' => []]], + tags: ['数据中心'], +)] +#[Permission(code: 'attachment:upload')] +#[ResultResponse(instance: new Result())] +public function upload(UploadRequest $request): Result +{ + $uploadFile = $request->file('file'); + $newTmpPath = sys_get_temp_dir() . '/' . uniqid() . '.' . $uploadFile->getExtension(); + $uploadFile->moveTo($newTmpPath); + $splFileInfo = new SplFileInfo($newTmpPath, '', ''); + return $this->success( + $this->service->upload($splFileInfo, $uploadFile, $this->currentUser->id()) + ); +} +``` + +```php [Service->Upload] + +namespace App\Service; + +use App\Model\Attachment; +use App\Repository\AttachmentRepository; +use Hyperf\HttpMessage\Upload\UploadedFile; +use Mine\Upload\UploadInterface; +use Symfony\Component\Finder\SplFileInfo; + +/** + * @extends IService + */ +final class AttachmentService extends IService +{ + public function __construct( + protected readonly AttachmentRepository $repository, + protected readonly UploadInterface $upload + ) {} + + public function upload(SplFileInfo $fileInfo, UploadedFile $uploadedFile, int $userId): Attachment + { + $fileHash = md5_file($fileInfo->getRealPath()); + if ($attachment = $this->repository->findByHash($fileHash)) { + return $attachment; + } + $upload = $this->upload->upload( + $fileInfo, + ); + return $this->repository->create([ + 'created_by' => $userId, + 'origin_name' => $uploadedFile->getClientFilename(), + 'storage_mode' => $upload->getStorageMode(), + 'object_name' => $upload->getObjectName(), + 'mime_type' => $upload->getMimeType(), + 'storage_path' => $upload->getStoragePath(), + 'hash' => $fileHash, + 'suffix' => $upload->getSuffix(), + 'size_byte' => $upload->getSizeByte(), + 'size_info' => $upload->getSizeInfo(), + 'url' => $upload->getUrl(), + ]); + } +``` + +::: + +### 替换本地存储为 Oss 存储 + +在日常的业务场景中,一般文件存储在 OSS 上。那么此时就需要替换默认的文件上传处理了。以阿里云举例 +首先我们要配置 `config/autoload/file.php` 文件。新增阿里云通道。 +然后新建一个 `AliyunUploadSubscribe` 替换 `config/autoload/listeners.php` 中默认的 `UploadSubscribe` 指定为阿里云通道 + + +::: code-group + +```php{19} [AliyunUploadSubscribe] + 'local', + 'storage' => [ + 'local' => [ + 'driver' => LocalAdapterFactory::class, + 'root' => BASE_PATH . '/storage/uploads', + 'public_url' => env('APP_URL') . '/uploads', + ], + 'oss' => [ + 'driver' => AliyunOssAdapterFactory::class, + 'accessId' => '', + 'accessSecret' => '', + 'bucket' => '', + 'endpoint' => '', + 'domain' => '', + 'schema' => 'http://', + 'isCName' => false, + // 'timeout' => 3600, + // 'connectTimeout' => 10, + // 'token' => '', + ], + ], +]; + +``` + +```php{20,25-28} [listeners.php] +// config/autoload/listeners.php +generatorPath()` 和 +`Mine\Upload\Listener\UploadListener->generatorId()` 实现的,而所有的上传处理类都是继承与这个类。 +那么以上一张替换 OSS 存储为例。只需要在你的上传处理类中替换这两个方法即可 + +```php{22-32} +isUploaded() 是否上传成功` +如果上传成功则返回 `Upload` 上传实例 +如果没有上传成功则抛出异常上传失败 + +#### 流程图 + +```plantuml +|前端| +start +:调用接口 /admin/attachment/upload,传入文件参数 file; +|服务端文件控制器| +:调用文件服务处理 file; +|文件服务| +:判断上传文件 hash 值是否已上传过; +if (已上传过) then (是) + :查询数据库返回上次上传信息; +else (否) + :调用 UploadInterface 实例分发 UploadEvent; + |UploadEvent| + :判断 isUploaded() 是否上传成功; + if (上传成功) then (是) + :返回 Upload 上传实例; + else (否) + :抛出异常上传失败; + endif +endif +``` + +#### 时序图 + +```plantuml +participant "前端" as Frontend +participant "服务端文件控制器" as Controller +participant "文件服务" as FileService +participant "UploadInterface" as Uploader +participant "数据库" as Database + +Frontend -> Controller : 调用接口 /admin/attachment/upload,传入 file +Controller -> FileService : 处理 file +FileService -> FileService : 判断文件 hash 值是否已上传过 +alt 已上传过 + FileService -> Database : 查询数据库 + Database -> FileService : 返回上次上传信息 + FileService -> Controller : 返回上次上传信息 + Controller -> Frontend : 返回上次上传信息 +else 未上传过 + FileService -> Uploader : 分发 UploadEvent + Uploader -> Uploader : 判断 isUploaded() 是否上传成功 + alt 上传成功 + Uploader -> FileService : 返回 Upload 上传实例 + FileService -> Controller : 返回 Upload 上传实例 + Controller -> Frontend : 返回 Upload 上传实例 + else 上传失败 + Uploader -> FileService : 抛出异常上传失败 + FileService -> Controller : 抛出异常上传失败 + Controller -> Frontend : 抛出异常上传失败 + end +end +``` + +## 前端直传 OSS \ No newline at end of file