229 lines
7.0 KiB
PHP
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;
|
|
}
|
|
}
|