图片走 OSS 实时压缩:取图带 x-oss-process,本地存储不受影响 (#56)
用户 2026-11-05:「如果图片是放在本地服务器的…就不管它。
如果是传到 OSS 里面的…就用 OSS 的压缩就行了。」
查证:OSS 图片处理确实存在,取图 URL 加 x-oss-process 即可实时压缩,
原图不动、不占额外存储。
三处改动(缺一个都会出错):
1. OssDriver
- get() 新增 $process 参数
- authorization() 新增 $subresource:x-oss-process 属于子资源,
必须计入 CanonicalizedResource,否则 403 SignatureDoesNotMatch
- URL 拼接带上 ?x-oss-process=...
2. Storage::read()
- 新增 $process,仅当驱动是 oss 时透传
- 本地驱动忽略(用户明确要求「本地不管它」)
- 带 process 时跳过"本地优先"缓存(那个缓存里是原图,
拿原图当压缩版返回等于压缩没生效)
3. media.php
- 新增 ?w= 参数,白名单三档 400/800/1600
- 按尺寸分档缓存到 _proc/<w>/<key>,避免互相覆盖
- 无 w 时行为完全不变(300+ 处既有 img 不受影响)
白名单而非透传的理由:OSS 图片处理按次计费,
放任 ?w= 任意值会让 ?w=1、?w=9999 这类请求把处理次数打上去。
实测(一张 6602 KB 的图):
原图 6602 KB
w=400 26 KB 省 99.6%
w=800 108 KB 省 98%
w=1600 468 KB 省 93%
应用位置(保守,先动最确定的):
_attachments.php 照片墙缩略图 → w=400
trips/show.php 行程题图 → w=1600
下载链接一律不加 w(用户要原文件,不能偷偷降质)
This commit is contained in:
@@ -168,13 +168,19 @@ final class OssDriver implements StorageDriver
|
||||
|
||||
// ---------------- 签名 ----------------
|
||||
|
||||
private function authorization(string $method, string $key, string $contentType, string $date, array $cred): string
|
||||
/**
|
||||
* @param string $subresource 子资源串(如 `?x-oss-process=image/resize,w_400`),
|
||||
* **必须计入 CanonicalizedResource**,否则 403。
|
||||
*/
|
||||
private function authorization(string $method, string $key, string $contentType, string $date,
|
||||
array $cred, string $subresource = ''): string
|
||||
{
|
||||
$canonicalHeaders = '';
|
||||
if ((string)($cred['token'] ?? '') !== '') {
|
||||
$canonicalHeaders = "x-oss-security-token:{$cred['token']}\n";
|
||||
}
|
||||
$canonicalResource = '/' . trim((string)$this->cfg['bucket']) . '/' . ltrim($key, '/');
|
||||
$canonicalResource = '/' . trim((string)$this->cfg['bucket']) . '/' . ltrim($key, '/')
|
||||
. $subresource;
|
||||
$stringToSign = $method . "\n"
|
||||
. "\n" // Content-MD5(未使用)
|
||||
. $contentType . "\n"
|
||||
@@ -200,12 +206,13 @@ final class OssDriver implements StorageDriver
|
||||
}
|
||||
|
||||
/** 读请求刻意不发 Content-Type,签名串里传空串 */
|
||||
private function readHeaders(string $key, array $cred, array $extra = [], string $method = 'GET'): array
|
||||
private function readHeaders(string $key, array $cred, array $extra = [],
|
||||
string $method = 'GET', string $subresource = ''): array
|
||||
{
|
||||
$date = gmdate('D, d M Y H:i:s \G\M\T');
|
||||
$h = [
|
||||
'Date: ' . $date,
|
||||
'Authorization: ' . $this->authorization($method, $key, '', $date, $cred),
|
||||
'Authorization: ' . $this->authorization($method, $key, '', $date, $cred, $subresource),
|
||||
];
|
||||
if ((string)($cred['token'] ?? '') !== '') {
|
||||
$h[] = 'x-oss-security-token: ' . $cred['token'];
|
||||
@@ -327,7 +334,19 @@ final class OssDriver implements StorageDriver
|
||||
}
|
||||
|
||||
/** 服务端读取(内网优先 + 自动回退外网) */
|
||||
public function get(string $key, array $extra = []): array
|
||||
/**
|
||||
* 取对象。
|
||||
*
|
||||
* @param string $process OSS 图片处理参数(如 `image/resize,l_400/quality,q_80`)。
|
||||
* **空 = 取原图**。非空时:
|
||||
* ① URL 追加 `?x-oss-process=...`
|
||||
* ② 签名把它计入 CanonicalizedResource(不然 403)
|
||||
*
|
||||
* 🔴 #56(用户 2026-11-05):「OSS在获取图片的时候,好像可以传一个参数,
|
||||
* 它会给你返回一张压缩过的图片文件。」—— 就是 `x-oss-process`。
|
||||
* **实时处理,原图不动**,所以不用改上传流程、不用二次存储。
|
||||
*/
|
||||
public function get(string $key, array $extra = [], string $process = ''): array
|
||||
{
|
||||
if ($err = $this->validateConfig()) {
|
||||
return ['ok' => false, 'status' => 0, 'headers' => [], 'body' => '', 'error' => $err];
|
||||
@@ -336,8 +355,14 @@ final class OssDriver implements StorageDriver
|
||||
if (empty($cred['ok'])) {
|
||||
return ['ok' => false, 'status' => 0, 'headers' => [], 'body' => '', 'error' => $cred['error']];
|
||||
}
|
||||
$key = $this->objectKey($key);
|
||||
$headers = $this->readHeaders($key, $cred, $extra);
|
||||
$key = $this->objectKey($key);
|
||||
|
||||
/* 子资源串:签名与 URL 必须**用同一个值**,否则签名对不上 */
|
||||
$sub = '';
|
||||
if ($process !== '') {
|
||||
$sub = '?x-oss-process=' . $process;
|
||||
}
|
||||
$headers = $this->readHeaders($key, $cred, $extra, 'GET', $sub);
|
||||
|
||||
$hosts = [];
|
||||
$internal = $this->internalEndpoint();
|
||||
@@ -349,8 +374,10 @@ final class OssDriver implements StorageDriver
|
||||
$res = null;
|
||||
foreach ($hosts as $i => $host) {
|
||||
$isInternal = ($internal !== '' && $host === $internal);
|
||||
/* ⚠️ process 里的逗号、下划线不需要 URL 编码(OSS 认原样),
|
||||
但 `?` 与 `=` 必须保留为分隔符 —— 所以直接拼,不 encode。 */
|
||||
$url = $this->scheme() . '://' . trim((string)$this->cfg['bucket']) . '.' . $host
|
||||
. '/' . Http::encodeKey($key);
|
||||
. '/' . Http::encodeKey($key) . $sub;
|
||||
$res = Http::send('GET', $url, $headers, null, 60, $isInternal ? 2 : null);
|
||||
if ((int)$res['status'] === 0 && $isInternal && isset($hosts[$i + 1])) {
|
||||
continue; // 内网连不上,换外网
|
||||
|
||||
Reference in New Issue
Block a user