1
This commit is contained in:
@@ -6,6 +6,17 @@ export type AssetReadUrlRequest = {
|
||||
expireSeconds?: number;
|
||||
};
|
||||
|
||||
type AssetReadUrlResolveOptions = {
|
||||
signal?: AbortSignal;
|
||||
expireSeconds?: number;
|
||||
/**
|
||||
* 图片内容可能在同一路径下被重新写入。
|
||||
* 这时需要显式跳过本地签名缓存,并在最终 URL 上追加一次性参数,
|
||||
* 避免结果页仍命中旧签名地址或浏览器图片缓存。
|
||||
*/
|
||||
refreshKey?: string | number | null;
|
||||
};
|
||||
|
||||
export type AssetReadUrlResponse = {
|
||||
read?: {
|
||||
objectKey?: string;
|
||||
@@ -100,21 +111,26 @@ function shouldReuseCachedReadUrlFailure(
|
||||
export async function getSignedAssetReadUrl(
|
||||
request: AssetReadUrlRequest,
|
||||
signal?: AbortSignal,
|
||||
options: {
|
||||
bypassCache?: boolean;
|
||||
} = {},
|
||||
) {
|
||||
const cacheKey = buildCacheKey(request);
|
||||
const cached = cacheKey ? signedReadUrlCache.get(cacheKey) : undefined;
|
||||
const bypassCache = options.bypassCache === true;
|
||||
const cached =
|
||||
!bypassCache && cacheKey ? signedReadUrlCache.get(cacheKey) : undefined;
|
||||
if (cached && shouldReuseCachedReadUrl(cached)) {
|
||||
return cached.signedUrl;
|
||||
}
|
||||
|
||||
const cachedFailure = cacheKey
|
||||
const cachedFailure = !bypassCache && cacheKey
|
||||
? signedReadUrlFailureCache.get(cacheKey)
|
||||
: undefined;
|
||||
if (cachedFailure && shouldReuseCachedReadUrlFailure(cachedFailure)) {
|
||||
throw new Error('资源不存在或暂时不可读取');
|
||||
}
|
||||
|
||||
if (cacheKey) {
|
||||
if (cacheKey && !bypassCache) {
|
||||
const pendingRequest = pendingSignedReadUrlRequests.get(cacheKey);
|
||||
if (pendingRequest) {
|
||||
return pendingRequest;
|
||||
@@ -178,26 +194,48 @@ export async function getSignedAssetReadUrl(
|
||||
}
|
||||
})();
|
||||
|
||||
if (cacheKey) {
|
||||
if (cacheKey && !bypassCache) {
|
||||
pendingSignedReadUrlRequests.set(cacheKey, requestPromise);
|
||||
}
|
||||
|
||||
try {
|
||||
return await requestPromise;
|
||||
} finally {
|
||||
if (cacheKey) {
|
||||
if (cacheKey && !bypassCache) {
|
||||
pendingSignedReadUrlRequests.delete(cacheKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function appendCacheBustParam(
|
||||
url: string,
|
||||
refreshKey: string | number | null | undefined,
|
||||
) {
|
||||
const normalizedRefreshKey =
|
||||
refreshKey === null || refreshKey === undefined
|
||||
? ''
|
||||
: String(refreshKey).trim();
|
||||
if (!normalizedRefreshKey) {
|
||||
return url;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsedUrl = new URL(url, globalThis.location?.origin ?? 'http://localhost');
|
||||
parsedUrl.searchParams.set('_v', normalizedRefreshKey);
|
||||
if (/^(?:https?:)?\/\//u.test(url)) {
|
||||
return parsedUrl.toString();
|
||||
}
|
||||
return `${parsedUrl.pathname}${parsedUrl.search}${parsedUrl.hash}`;
|
||||
} catch {
|
||||
const separator = url.includes('?') ? '&' : '?';
|
||||
return `${url}${separator}_v=${encodeURIComponent(normalizedRefreshKey)}`;
|
||||
}
|
||||
}
|
||||
|
||||
// 兼容层:普通 http(s)/data/blob 路径原样返回;历史 generated-* 路径自动换签名读 URL。
|
||||
export async function resolveAssetReadUrl(
|
||||
source: string | null | undefined,
|
||||
options: {
|
||||
signal?: AbortSignal;
|
||||
expireSeconds?: number;
|
||||
} = {},
|
||||
options: AssetReadUrlResolveOptions = {},
|
||||
) {
|
||||
const value = source?.trim() ?? '';
|
||||
if (!value) {
|
||||
@@ -209,20 +247,25 @@ export async function resolveAssetReadUrl(
|
||||
value.startsWith('data:') ||
|
||||
value.startsWith('blob:')
|
||||
) {
|
||||
return value;
|
||||
return appendCacheBustParam(value, options.refreshKey);
|
||||
}
|
||||
|
||||
if (isGeneratedLegacyPath(value)) {
|
||||
return getSignedAssetReadUrl(
|
||||
const signedUrl = await getSignedAssetReadUrl(
|
||||
{
|
||||
legacyPublicPath: value,
|
||||
expireSeconds: options.expireSeconds,
|
||||
},
|
||||
options.signal,
|
||||
{
|
||||
bypassCache:
|
||||
options.refreshKey !== null && options.refreshKey !== undefined,
|
||||
},
|
||||
);
|
||||
return appendCacheBustParam(signedUrl, options.refreshKey);
|
||||
}
|
||||
|
||||
return value;
|
||||
return appendCacheBustParam(value, options.refreshKey);
|
||||
}
|
||||
|
||||
export function clearSignedAssetReadUrlCache() {
|
||||
|
||||
Reference in New Issue
Block a user