import { useEffect, useState } from 'react'; import { getSignedAssetReadUrl, resolveAssetReadUrl, shouldResolveAssetReadUrl, } from '../services/assetReadUrlService'; type UseResolvedAssetReadUrlOptions = { enabled?: boolean; expireSeconds?: number; objectKey?: string | null; refreshKey?: string | number | null; }; export function useResolvedAssetReadUrl( source: string | null | undefined, options: UseResolvedAssetReadUrlOptions = {}, ) { const enabled = options.enabled !== false; const normalizedSource = source?.trim() ?? ''; const normalizedObjectKey = options.objectKey?.trim() ?? ''; const shouldResolve = enabled && (Boolean(normalizedObjectKey) || shouldResolveAssetReadUrl(normalizedSource)); const [resolvedUrl, setResolvedUrl] = useState( shouldResolve ? '' : normalizedSource, ); const [isResolving, setIsResolving] = useState(shouldResolve); useEffect(() => { if (!normalizedSource && !normalizedObjectKey) { setResolvedUrl(''); setIsResolving(false); return; } if (!shouldResolve) { setResolvedUrl(normalizedSource); setIsResolving(false); return; } let cancelled = false; // 生成资源通常是 OSS 私有对象;签名 URL 未就绪前不能把裸 generated 路径交给 img 触发无鉴权 GET。 setResolvedUrl(''); setIsResolving(true); const resolvePromise = normalizedObjectKey ? getSignedAssetReadUrl( { objectKey: normalizedObjectKey, expireSeconds: options.expireSeconds, }, undefined, { cacheVersion: options.refreshKey, }, ) : resolveAssetReadUrl(normalizedSource, { expireSeconds: options.expireSeconds, refreshKey: options.refreshKey, }); void resolvePromise .then((nextUrl) => { if (!cancelled) { setResolvedUrl(nextUrl); } }) .catch(() => { if (!cancelled) { // 签名失败时保持空 src,避免继续请求无签名的私有对象兼容路径。 setResolvedUrl(''); } }) .finally(() => { if (!cancelled) { setIsResolving(false); } }); return () => { cancelled = true; }; }, [ normalizedSource, normalizedObjectKey, options.expireSeconds, options.refreshKey, shouldResolve, ]); return { resolvedUrl, isResolving, shouldResolve, }; }