阿里雲 OSS (Object Storage Service) 是雲端物件儲存服務。Laravel 官方不內建 OSS 驅動,社區優秀封裝 johnlui/aliyun-oss 提供簡潔的 API。本文詳解安裝、配置、上傳、下載、簽名 URL、多 Bucket、錯誤處理與生產級最佳實踐。
composer require johnlui/aliyun-oss
支援 Laravel 5.5+ 自動發現 ServiceProvider。Laravel 5.4 需手動註冊:
// config/app.php
'providers' => [
JohnLui\AliyunOSS\AliyunOSSServiceProvider::class,
],
'aliases' => [
'OSS' => JohnLui\AliyunOSS\Facades\AliyunOSS::class,
]
# .env
OSS_ENDPOINT=oss-cn-hangzhou.aliyuncs.com
OSS_ACCESS_KEY_ID=your_access_key_id
OSS_ACCESS_KEY_SECRET=your_access_key_secret
OSS_BUCKET=your-bucket-name
OSS_CDN_DOMAIN=cdn.yourdomain.com # 可選:自定義 CDN 域名
# config/filesystems.php (可選整合到 Flysystem)
'disks' => [
'oss' => [
'driver' => 'oss',
'endpoint' => env('OSS_ENDPOINT'),
'access_key_id' => env('OSS_ACCESS_KEY_ID'),
'access_key_secret' => env('OSS_ACCESS_KEY_SECRET'),
'bucket' => env('OSS_BUCKET'),
],
]
use OSS;
// 上傳檔案
$result = OSS::uploadFile('avatars/user_1.jpg', storage_path('app/public/avatar.jpg'));
// $result = ['url' => 'https://bucket.oss-cn-hangzhou.aliyuncs.com/avatars/user_1.jpg', ...]
// 上傳字串內容
OSS::uploadContent('config/app.json', '{"debug": true}');
// 獲取簽名 URL (私有桶臨時授權)
$signedUrl = OSS::signUrl('private/report.pdf', '+10 minutes'); // 10 分鐘有效
// 刪除檔案
OSS::deleteObject('old/temp.jpg');
// 判斷存在
if (OSS::objectExists('avatars/user_1.jpg')) { ... }
// 獲取檔案元資料
$meta = OSS::getObjectMeta('avatars/user_1.jpg');
// ['content-length', 'content-type', 'last-modified', 'etag', ...]
use JohnLui\AliyunOSS\AliyunOSS;
class AvatarService
{
protected AliyunOSS $oss;
public function __construct(AliyunOSS $oss)
{
$this->oss = $oss->setBucket(config('filesystems.disks.oss.bucket'));
}
public function upload(UploadedFile $file, int $userId): string
{
$key = "avatars/user_{$userId}_" . time() . '.' . $file->getClientOriginalExtension();
$this->oss->uploadFile($key, $file->getPathname());
return $this->oss->getUrl($key); // 公開讀取 URL
}
public function getSignedUrl(string $key, int $minutes = 60): string
{
return $this->oss->getUrl($key, now()->addMinutes($minutes));
}
}
// 手動分片上傳 (SDK 內部已封裝 multipart upload)
$oss->uploadFile('large/video.mp4', '/path/to/large/video.mp4', [
'partSize' => 10 * 1024 * 1024, // 10MB 分片
'concurrency' => 3,
'progress' => function ($bytes, $total) {
echo "Progress: " . round($bytes / $total * 100, 2) . "%\n";
}
]);
// 原圖處理參數
$processedUrl = $oss->getUrl('images/photo.jpg') . '?x-oss-process=image/resize,w_300,h_200/quality,q_80/format,webp';
// 等同:https://bucket.oss-cn-hangzhou.aliyuncs.com/images/photo.jpg?x-oss-process=image/resize,w_300,h_200/quality,q_80/format,webp
// 樣式分隔符 (需 OSS 控制台建立樣式)
$styleUrl = $oss->getUrl('images/photo.jpg') . '!thumbnail';
// 後端生成 Policy + Signature
$policy = $oss->getPolicy([
'expiration' => now()->addMinutes(30)->toISOString(),
'conditions' => [
['content-length-range', 0, 10 * 1024 * 1024], // 限制 10MB
['starts-with', '$key', 'uploads/user_123/'],
],
]);
// 返回給前端:policy (base64), signature, access_key_id, endpoint, bucket, key 前綴
// 控制台配置:ObjectCreated -> HTTP 回調到你的 API
// 回調驗證
$auth = request()->header('Authorization');
$body = request()->getContent();
// 驗證簽名...
// 異步處理:轉碼、縮圖、入庫...
use OSS\Core\OssException;
try {
$url = OSS::uploadFile('test.jpg', $path);
} catch (OssException $e) {
// $e->getMessage() 包含 HTTP 狀態碼、OSS 錯誤碼、RequestId
// 常見錯誤碼:
// NoSuchBucket, AccessDenied, InvalidAccessKeyId, SignatureDoesNotMatch
// RequestTimeTooSkewed (時間不同步), EntityTooLarge (>5GB)
Log::error('OSS Upload Failed', [
'error' => $e->getMessage(),
'code' => $e->getCode(),
'request_id' => $e->getRequestId(),
]);
// 可重試邏輯 (網路抖動、5xx)
if (in_array($e->getCode(), ['RequestTimeout', 'InternalError', '503'])) {
// 指數退避重試...
}
throw $e;
}
// 動態切換 Bucket
$oss = OSS::boot($endpoint, $keyId, $keySecret);
$oss->setBucket('bucket-a')->uploadFile('a.txt', $pathA);
$oss->setBucket('bucket-b')->uploadFile('b.txt', $pathB);
// 或建立多個實例
$ossA = new AliyunOSS($endpointA, $keyId, $keySecret)->setBucket('bucket-a');
$ossB = new AliyunOSS($endpointB, $keyId, $keySecret)->setBucket('bucket-b');
// composer require league/flysystem-aws-s3-v3 (OSS 兼容 S3 API)
// config/filesystems.php
'oss' => [
'driver' => 's3',
'key' => env('OSS_ACCESS_KEY_ID'),
'secret' => env('OSS_ACCESS_KEY_SECRET'),
'region' => 'oss-cn-hangzhou', // 非標準 region,但需填寫
'bucket' => env('OSS_BUCKET'),
'endpoint' => env('OSS_ENDPOINT'),
'url' => env('OSS_CDN_DOMAIN'), // CDN 域名
'visibility' => 'public',
],
// 使用
Storage::disk('oss')->put('file.txt', 'content');
Storage::disk('oss')->url('file.txt');
| 項目 | 建議 |
|---|---|
| 權限最小化 | RAM 子帳號僅授予特定 Bucket 的 PutObject/GetObject/DeleteObject |
| HTTPS 強制 | Endpoint 使用 https://,CDN 全站 HTTPS |
| 防盜鏈 | OSS 控制台配置 Referer 白名單 |
| 生命週期規則 | 自動轉冷存儲/刪除臨時檔案 |
| 跨域 (CORS) | 直傳需配置 AllowedOrigin, AllowedMethod, AllowedHeader, ExposeHeader: ETag |
| 監控告警 | 關注:請求失敗率、延遲 P99、流量計費、儲存量增長 |
| 備份策略 | 跨區域複制 (CRR) 至備份 Bucket |
| 時間同步 | 伺服器啟用 NTP,避免 RequestTimeTooSkewed |
SignatureDoesNotMatchmax_execution_time、Nginx client_max_body_size、啟用分片上傳NoSuchKeyjohnlui/aliyun-oss 以極簡 API 封裝了阿里雲 OSS SDK 核心功能,適合中小型專案快速接入。大型專案或需深度定製 (自定義重試、簽名 V4、VPC 專線) 時,建議直接使用官方 aliyuncs/oss-sdk-php。