Files
fdesk/app/common/service/OssService.php
T
mzl92 2de6342058 fix: 备货单车辆附件图片过期打不开——url 改为响应期重签
原因:attachment[].url 存的是上传时生成的带签名OSS地址(默认1小时
有效),过期后 AccessDenied/Request has expired。
修复:OssService 新增 resignAttachmentUrls(),从存量 url(完整签名URL/
未签名URL/纯对象键三种格式)提取对象键重新签名(7天);
detail(prepareVehicleListData)与 show(crm/admin 两控制器 showFormat
的 items)三处响应时统一重签。存储不动,前端无需改。

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-26 11:51:40 +08:00

149 lines
4.8 KiB
PHP

<?php
namespace app\common\service;
use OSS\OssClient;
use OSS\Core\OssException;
use support\exception\BusinessException;
class OssService
{
private $accessKeyId;
private $accessKeySecret;
private $bucket;
private $endpoint;
private $ossClient;
public function __construct()
{
$this->accessKeyId = getenv('OSS_ACCESSID');
$this->accessKeySecret = getenv('OSS_ACCESSSECRET');
$this->bucket = getenv('OSS_BUCKET');
$this->endpoint = getenv('OSS_ENDPOINT');
$this->ossClient = new OssClient($this->accessKeyId, $this->accessKeySecret, $this->endpoint);
}
/**
* 上传图片到 OSS
* @param string $filePath 本地文件路径
* @param string $objectKey OSS上存储的对象名称
* @return string 图片的 URL
*/
public function uploadImage($filePath, $objectKey)
{
try {
// 上传文件到 OSS
$this->ossClient->uploadFile($this->bucket, $objectKey, $filePath);
// 构造图片的私有 URL
$url = $this->generatePrivateUrl($objectKey);
return $url;
} catch (OssException $e) {
// 错误处理
throw new \RuntimeException('OSS Error: ' . $e->getMessage());
} catch (\Throwable $th) {
// 错误处理
throw new BusinessException('Error: ' . $th->getMessage());
}
}
/**
* 生成图片的私有访问 URL
* @param string $objectKey
* @return string
*/
public function generatePrivateUrl($objectKey, $expireTime = 3600)
{
// 生成 OSS 私有 URL(有效期为 3600 秒)
return $this->ossClient->signUrl($this->bucket, $objectKey, $expireTime);
}
/**
* 附件数组的 url 重签为新的私有访问地址(响应期调用,防止历史签名过期)。
* 兼容三种存量格式:完整签名URL / 完整未签名URL / 纯对象键。
* @param array $attachments [{url, original_name}, ...]
* @param int $expireTime
* @return array
*/
public static function resignAttachmentUrls(array $attachments, int $expireTime = 604800): array
{
foreach ($attachments as &$a) {
if (empty($a['url']) || !is_string($a['url'])) {
continue;
}
try {
$url = $a['url'];
if (strpos($url, 'http') === 0) {
$path = (string)parse_url($url, PHP_URL_PATH);
if ($path === '') {
continue;
}
$key = ltrim(urldecode($path), '/');
} else {
$key = ltrim($url, '/'); // 已是对象键
}
if ($key === '') {
continue;
}
$a['url'] = (new self())->generatePrivateUrl($key, $expireTime);
} catch (\Throwable $e) {
// 重签失败保留原样
}
}
unset($a);
return $attachments;
}
/**
* 删除 OSS 上的文件
* @param string $objectKey OSS上存储的对象名称
* @return bool 删除是否成功
*/
public function deleteFile($objectKey)
{
try {
// 删除 OSS 上的文件
$this->ossClient->deleteObject($this->bucket, $objectKey);
return true;
} catch (OssException $e) {
// 错误处理
throw new \RuntimeException('OSS Delete Error: ' . $e->getMessage());
} catch (\Throwable $th) {
// 错误处理
throw new BusinessException('Delete Error: ' . $th->getMessage());
}
}
/**
* 获取对应的 MIME 类型
*/
public function getMimeTypes($extensions)
{
$mimeMap = [
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'bmp' => 'image/bmp',
'webp' => 'image/webp',
'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',
'ppt' => 'application/vnd.ms-powerpoint',
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'txt' => 'text/plain',
'zip' => 'application/zip',
'rar' => 'application/x-rar-compressed'
];
$mimes = [];
foreach ($extensions as $ext) {
if (isset($mimeMap[$ext])) {
$mimes[] = $mimeMap[$ext];
}
}
return array_unique($mimes);
}
}