Files
lgp-admin-plus-api/app/Service/common/upload/QiniuStorageService.php

178 lines
6.0 KiB
PHP
Raw Normal View History

2025-05-12 00:19:35 +08:00
<?php
namespace App\Service\common\upload;
2026-08-10 18:32:06 +08:00
use App\BaseApp\BaseNotAuthService;
use App\Service\common\oss\OssStorageInterface;
use App\Service\common\UtilsService;
2025-05-12 00:19:35 +08:00
2026-08-10 18:32:06 +08:00
/**
* 七牛云上传;优先使用官方 SDK未安装时给出明确提示
*/
class QiniuStorageService extends BaseNotAuthService implements OssStorageInterface
2025-05-12 00:19:35 +08:00
{
use ObjectKeyNormalizeTrait;
2026-08-10 18:32:06 +08:00
protected array $config = [
'access_key' => '',
'secret_key' => '',
'bucket' => '',
'domain' => '',
'path_prefix' => '',
];
2025-05-12 00:19:35 +08:00
/**
2026-08-10 18:32:06 +08:00
* 注入解密后的运行时配置
2025-05-12 00:19:35 +08:00
*/
2026-08-10 18:32:06 +08:00
public function withConfig(array $config): static
2025-05-12 00:19:35 +08:00
{
2026-08-10 18:32:06 +08:00
$this->config = array_merge($this->config, $config);
return $this;
2025-05-12 00:19:35 +08:00
}
2026-08-10 18:32:06 +08:00
public function uploadImage($filePath, string $key): bool|array
2025-05-12 00:19:35 +08:00
{
return $this->uploadFile($filePath, $key);
}
2026-08-10 18:32:06 +08:00
public function uploadVideo($filePath, string $key): bool|array
2025-05-12 00:19:35 +08:00
{
return $this->uploadFile($filePath, $key);
}
public function deleteImage($key): bool
{
return $this->deleteFile($key);
}
public function deleteVideo($key): bool
{
return $this->deleteFile($key);
}
/**
* 列举空间对象BucketManager::listFilesmarker 分页)
*
* 七牛只在还有下一页时才回 marker所以 marker 为空即等于列完了。
*/
public function listObjects(string $prefix = '', string $marker = '', int $limit = 100): array
{
$bucketMgr = $this->bucketManager();
$bucket = (string) ($this->config['bucket'] ?? '');
[$ret, $err] = $bucketMgr->listFiles(
$bucket,
$this->scopedListPrefix($prefix),
$marker !== '' ? $marker : null,
$this->boundedLimit($limit)
);
if ($err !== null) {
UtilsService::getInstance()->errorThrow('七牛云列举对象失败:' . $this->errorText($err));
}
$items = [];
foreach ((array) ($ret['items'] ?? []) as $row) {
$key = $this->normalizeObjectKey((string) ($row['key'] ?? ''));
if ($key === '' || str_ends_with($key, '/')) {
continue;
}
$items[] = [
'key' => $key,
'size' => (int) ($row['fsize'] ?? 0),
'hash' => (string) ($row['hash'] ?? ''),
// putTime 的单位是 100 纳秒,当秒用会得到五亿年后的时间戳
'last_modified' => intdiv((int) ($row['putTime'] ?? 0), 10000000),
'url' => $this->publicUrlOf($key, $key),
];
}
$next = (string) ($ret['marker'] ?? '');
return [
'items' => $items,
'next_marker' => $next,
'finished' => $next === '',
];
}
public function deleteObject(string $key): bool
{
return $this->deleteFile($this->normalizeObjectKey($key));
}
2025-05-12 00:19:35 +08:00
/**
2026-08-10 18:32:06 +08:00
* 上传到七牛;无 SDK 时抛业务异常引导安装或改本地
2025-05-12 00:19:35 +08:00
*/
2026-08-10 18:32:06 +08:00
private function uploadFile($filePath, string $key): bool|array
2025-05-12 00:19:35 +08:00
{
2026-08-10 18:32:06 +08:00
if (!class_exists(\Qiniu\Auth::class)) {
UtilsService::getInstance()->errorThrow('未安装 qiniu/php-sdk请改用本地存储或安装依赖');
}
$accessKey = (string) ($this->config['access_key'] ?? '');
$secretKey = (string) ($this->config['secret_key'] ?? '');
$bucket = (string) ($this->config['bucket'] ?? '');
$domain = rtrim((string) ($this->config['domain'] ?? ''), '/');
if ($accessKey === '' || $secretKey === '' || $bucket === '') {
UtilsService::getInstance()->errorThrow('七牛云配置不完整');
}
$prefix = trim((string) ($this->config['path_prefix'] ?? ''), '/');
if ($prefix !== '' && !str_starts_with($key, $prefix . '/')) {
$key = $prefix . '/' . ltrim($key, '/');
}
$path = is_object($filePath) && method_exists($filePath, 'getRealPath')
? $filePath->getRealPath()
: (string) $filePath;
$auth = new \Qiniu\Auth($accessKey, $secretKey);
$token = $auth->uploadToken($bucket);
$uploadMgr = new \Qiniu\Storage\UploadManager();
list($ret, $err) = $uploadMgr->putFile($token, $key, $path);
2025-05-12 00:19:35 +08:00
if ($err !== null) {
return false;
}
2026-08-10 18:32:06 +08:00
return [
'key' => $ret['key'],
'url' => $domain . '/' . $ret['key'],
];
2025-05-12 00:19:35 +08:00
}
private function deleteFile(string $key): bool
{
2026-08-10 18:32:06 +08:00
if (!class_exists(\Qiniu\Auth::class)) {
return false;
}
$auth = new \Qiniu\Auth($this->config['access_key'], $this->config['secret_key']);
$bucketMgr = new \Qiniu\Storage\BucketManager($auth);
$err = $bucketMgr->delete($this->config['bucket'], $key);
2025-05-12 00:19:35 +08:00
return $err === null;
}
/**
* 构造 BucketManager顺手把「没装 SDK / 没填配置」两种情况前置拦掉
*/
private function bucketManager(): \Qiniu\Storage\BucketManager
{
if (!class_exists(\Qiniu\Auth::class)) {
UtilsService::getInstance()->errorThrow('未安装 qiniu/php-sdk请改用本地存储或安装依赖');
}
$accessKey = (string) ($this->config['access_key'] ?? '');
$secretKey = (string) ($this->config['secret_key'] ?? '');
$bucket = (string) ($this->config['bucket'] ?? '');
if ($accessKey === '' || $secretKey === '' || $bucket === '') {
UtilsService::getInstance()->errorThrow('七牛云配置不完整');
}
return new \Qiniu\Storage\BucketManager(new \Qiniu\Auth($accessKey, $secretKey));
}
/**
* SDK 的错误对象没有统一契约,转成人能看懂的一行字
*/
private function errorText(mixed $err): string
{
if (is_object($err) && method_exists($err, 'message')) {
return (string) $err->message();
}
if (is_object($err) || is_array($err)) {
return (string) json_encode($err, JSON_UNESCAPED_UNICODE);
}
return (string) $err;
}
2025-05-12 00:19:35 +08:00
}