1
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import { parseApiErrorMessage } from '../../packages/shared/src/http';
|
||||
import {
|
||||
ApiClientError,
|
||||
BACKGROUND_AUTH_REQUEST_OPTIONS,
|
||||
type ApiRequestOptions,
|
||||
fetchWithApiAuth,
|
||||
requestJson,
|
||||
} from './apiClient';
|
||||
|
||||
@@ -23,6 +25,11 @@ type AssetReadUrlResolveOptions = {
|
||||
refreshKey?: string | number | null;
|
||||
};
|
||||
|
||||
type AssetReadBytesOptions = {
|
||||
signal?: AbortSignal;
|
||||
expireSeconds?: number;
|
||||
};
|
||||
|
||||
export type AssetReadUrlResponse = {
|
||||
read?: {
|
||||
objectKey?: string;
|
||||
@@ -44,6 +51,7 @@ type CachedReadUrlFailureEntry = {
|
||||
};
|
||||
|
||||
const ASSET_READ_URL_API_PATH = '/api/assets/read-url';
|
||||
const ASSET_READ_BYTES_API_PATH = '/api/assets/read-bytes';
|
||||
const DEFAULT_CACHE_SAFETY_WINDOW_MS = 30 * 1000;
|
||||
const DEFAULT_FAILURE_CACHE_WINDOW_MS = 60 * 1000;
|
||||
const ASSET_READ_URL_BACKGROUND_OPTIONS =
|
||||
@@ -72,6 +80,27 @@ function buildCacheKey(request: AssetReadUrlRequest) {
|
||||
return '';
|
||||
}
|
||||
|
||||
function buildAssetReadSearchParams(request: AssetReadUrlRequest) {
|
||||
const searchParams = new URLSearchParams();
|
||||
if (request.objectKey?.trim()) {
|
||||
searchParams.set('objectKey', request.objectKey.trim().replace(/^\/+/u, ''));
|
||||
}
|
||||
if (request.legacyPublicPath?.trim()) {
|
||||
searchParams.set(
|
||||
'legacyPublicPath',
|
||||
normalizeLegacyPublicPath(request.legacyPublicPath),
|
||||
);
|
||||
}
|
||||
if (
|
||||
typeof request.expireSeconds === 'number' &&
|
||||
Number.isFinite(request.expireSeconds) &&
|
||||
request.expireSeconds > 0
|
||||
) {
|
||||
searchParams.set('expireSeconds', String(Math.floor(request.expireSeconds)));
|
||||
}
|
||||
return searchParams;
|
||||
}
|
||||
|
||||
function resolveSignedReadPayload(response: AssetReadUrlResponse) {
|
||||
const read = response.read ?? response;
|
||||
const signedUrl = typeof read.signedUrl === 'string' ? read.signedUrl.trim() : '';
|
||||
@@ -146,23 +175,7 @@ export async function getSignedAssetReadUrl(
|
||||
}
|
||||
|
||||
const requestPromise = (async () => {
|
||||
const searchParams = new URLSearchParams();
|
||||
if (request.objectKey?.trim()) {
|
||||
searchParams.set('objectKey', request.objectKey.trim().replace(/^\/+/u, ''));
|
||||
}
|
||||
if (request.legacyPublicPath?.trim()) {
|
||||
searchParams.set(
|
||||
'legacyPublicPath',
|
||||
normalizeLegacyPublicPath(request.legacyPublicPath),
|
||||
);
|
||||
}
|
||||
if (
|
||||
typeof request.expireSeconds === 'number' &&
|
||||
Number.isFinite(request.expireSeconds) &&
|
||||
request.expireSeconds > 0
|
||||
) {
|
||||
searchParams.set('expireSeconds', String(Math.floor(request.expireSeconds)));
|
||||
}
|
||||
const searchParams = buildAssetReadSearchParams(request);
|
||||
|
||||
try {
|
||||
const response = await requestJson<AssetReadUrlResponse>(
|
||||
@@ -289,6 +302,49 @@ export async function resolveAssetReadUrl(
|
||||
return appendCacheBustParam(value, options.refreshKey);
|
||||
}
|
||||
|
||||
export async function readAssetBytes(
|
||||
source: string | null | undefined,
|
||||
options: AssetReadBytesOptions = {},
|
||||
) {
|
||||
const value = source?.trim() ?? '';
|
||||
if (!value) {
|
||||
throw new Error('资源路径不能为空');
|
||||
}
|
||||
|
||||
if (!isGeneratedLegacyPath(value)) {
|
||||
const response = await fetch(value, { signal: options.signal });
|
||||
if (!response.ok) {
|
||||
throw new Error('读取资源内容失败');
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
// 中文注释:这里要拿图片字节转 Data URL,不能直接 fetch OSS 签名 URL,否则浏览器会受 bucket CORS 限制。
|
||||
const searchParams = buildAssetReadSearchParams({
|
||||
legacyPublicPath: value,
|
||||
expireSeconds: options.expireSeconds,
|
||||
});
|
||||
const response = await fetchWithApiAuth(
|
||||
`${ASSET_READ_BYTES_API_PATH}?${searchParams.toString()}`,
|
||||
{
|
||||
method: 'GET',
|
||||
signal: options.signal,
|
||||
},
|
||||
{
|
||||
...ASSET_READ_URL_BACKGROUND_OPTIONS,
|
||||
omitEnvelopeHeader: true,
|
||||
},
|
||||
);
|
||||
if (!response.ok) {
|
||||
const message = await response
|
||||
.text()
|
||||
.then((text) => parseApiErrorMessage(text, '读取资源内容失败'))
|
||||
.catch(() => '');
|
||||
throw new Error(message || '读取资源内容失败');
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
export function clearSignedAssetReadUrlCache() {
|
||||
signedReadUrlCache.clear();
|
||||
signedReadUrlFailureCache.clear();
|
||||
|
||||
Reference in New Issue
Block a user