Files
x-server-mobile/backend/app/service/OssService.php
T
2026-03-06 11:49:11 +08:00

229 lines
7.0 KiB
PHP

<?php
/**
* 阿里云 OSS 服务(在最开始设置错误报告)
*/
namespace app\Service;
class OssService
{
private string $accessKeyId;
private string $accessKeySecret;
private string $bucket;
private string $endpoint;
private string $staticUrl;
private string $mainDir;
private $ossClient = null;
public function __construct()
{
// 在构造函数开始时就禁用 deprecated 警告
$oldLevel = error_reporting(E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED);
try {
$this->accessKeyId = getenv('OSS_ACCESSID') ?: '';
$this->accessKeySecret = getenv('OSS_ACCESSSECRET') ?: '';
$this->bucket = getenv('OSS_BUCKET') ?: '';
$this->endpoint = getenv('OSS_ENDPOINT') ?: '';
$this->staticUrl = getenv('OSS_STATIC_URL') ?: '';
$this->mainDir = rtrim(getenv('OSS_MAIN_DIR') ?: 'uploads', '/');
if (empty($this->accessKeyId) || empty($this->accessKeySecret) || empty($this->bucket) || empty($this->endpoint)) {
throw new \Exception('OSS 配置不完整,请检查 .env 文件');
}
} finally {
error_reporting($oldLevel);
}
}
/**
* 获取 OSS Client 实例(延迟加载 + 错误抑制)
*/
private function getClient()
{
if ($this->ossClient === null) {
$oldLevel = error_reporting(E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED);
try {
$this->ossClient = new \OSS\OssClient($this->accessKeyId, $this->accessKeySecret, $this->endpoint);
} catch (\Throwable $e) {
throw new \Exception('OSS 初始化失败: ' . $e->getMessage());
} finally {
error_reporting($oldLevel);
}
}
return $this->ossClient;
}
/**
* 上传文件到 OSS
*
* @param string $filePath 本地文件路径
* @param string $objectKey OSS上存储的对象名称
* @return string 文件的公共访问 URL
* @throws \Exception
*/
public function uploadFile(string $filePath, string $objectKey): string
{
$oldLevel = error_reporting(E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED);
try {
$options = [
\OSS\OssClient::OSS_HEADERS => [
'Content-Type' => $this->getMimeType($filePath),
],
];
$this->getClient()->uploadFile($this->bucket, $objectKey, $filePath, $options);
return $this->getPublicUrl($objectKey);
} catch (\OSS\Core\OssException $e) {
throw new \Exception('OSS 上传失败: ' . $e->getMessage());
} catch (\Throwable $e) {
throw new \Exception('OSS 上传失败: ' . $e->getMessage());
} finally {
error_reporting($oldLevel);
}
}
/**
* 删除 OSS 上的文件
*
* @param string $objectKey OSS上存储的对象名称
* @return bool
* @throws \Exception
*/
public function deleteFile(string $objectKey): bool
{
$oldLevel = error_reporting(E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED);
try {
$this->getClient()->deleteObject($this->bucket, $objectKey);
return true;
} catch (\OSS\Core\OssException $e) {
throw new \Exception('OSS 删除失败: ' . $e->getMessage());
} catch (\Throwable $e) {
throw new \Exception('OSS 删除失败: ' . $e->getMessage());
} finally {
error_reporting($oldLevel);
}
}
/**
* 批量删除文件
*
* @param array $objectKeys 对象名称数组
* @return bool
* @throws \Exception
*/
public function deleteFiles(array $objectKeys): bool
{
$oldLevel = error_reporting(E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED);
try {
$this->getClient()->deleteObjects($this->bucket, $objectKeys);
return true;
} catch (\OSS\Core\OssException $e) {
throw new \Exception('OSS 批量删除失败: ' . $e->getMessage());
} catch (\Throwable $e) {
throw new \Exception('OSS 批量删除失败: ' . $e->getMessage());
} finally {
error_reporting($oldLevel);
}
}
/**
* 获取文件的公共访问 URL
*
* @param string $objectKey
* @return string
*/
public function getPublicUrl(string $objectKey): string
{
if (!empty($this->staticUrl)) {
return rtrim($this->staticUrl, '/') . '/' . $objectKey;
}
return 'https://' . $this->bucket . '.' . str_replace('https://', '', $this->endpoint) . '/' . $objectKey;
}
/**
* 生成带签名的访问 URL(用于私有bucket)
*
* @param string $objectKey
* @param int $timeout 有效期(秒)
* @return string
*/
public function getSignedUrl(string $objectKey, int $timeout = 3600): string
{
$oldLevel = error_reporting(E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED);
try {
return $this->getClient()->signUrl($this->bucket, $objectKey, $timeout);
} catch (\Throwable $e) {
return $this->getPublicUrl($objectKey);
} finally {
error_reporting($oldLevel);
}
}
/**
* 获取完整的对象键(包含主目录)
*
* @param string $path 相对路径
* @return string
*/
public function getObjectKey(string $path): string
{
$path = ltrim($path, '/');
return $this->mainDir ? ($this->mainDir . '/' . $path) : $path;
}
/**
* 检查 OSS 是否已配置
*
* @return bool
*/
public static function isConfigured(): bool
{
return !empty(getenv('OSS_ACCESSID'))
&& !empty(getenv('OSS_ACCESSSECRET'))
&& !empty(getenv('OSS_BUCKET'))
&& !empty(getenv('OSS_ENDPOINT'));
}
/**
* 获取文件的 MIME 类型
*
* @param string $filePath
* @return string
*/
private function getMimeType(string $filePath): string
{
$extension = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
$mimeMap = [
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'webp' => 'image/webp',
'bmp' => 'image/bmp',
'pdf' => 'application/pdf',
'doc' => 'application/msword',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'xls' => 'application/vnd.ms-excel',
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'txt' => 'text/plain',
'zip' => 'application/zip',
];
return $mimeMap[$extension] ?? 'application/octet-stream';
}
/**
* 获取 OSS 主目录
*
* @return string
*/
public function getMainDir(): string
{
return $this->mainDir;
}
}