65 lines
1.6 KiB
TypeScript
65 lines
1.6 KiB
TypeScript
export const DEFAULT_IMAGE_CACHE = 'images-v1'
|
|
|
|
const objectUrlMap = new Map<string, string>()
|
|
|
|
export async function getCachedImageUrl(
|
|
imageId: string,
|
|
cacheName = DEFAULT_IMAGE_CACHE,
|
|
): Promise<string> {
|
|
if (!imageId) throw new Error('imageId required')
|
|
|
|
// reuse existing object URL if created in this session
|
|
const existing = objectUrlMap.get(imageId)
|
|
if (existing) return existing
|
|
|
|
const requestUrl = `/api/image/request/${imageId}`
|
|
|
|
// Try Cache Storage first
|
|
let response: Response | undefined
|
|
if ('caches' in window) {
|
|
const cache = await caches.open(cacheName)
|
|
response = await cache.match(requestUrl)
|
|
if (!response) {
|
|
const fetched = await fetch(requestUrl)
|
|
if (!fetched.ok) throw new Error(`HTTP ${fetched.status}`)
|
|
// store a clone in Cache Storage (non-blocking)
|
|
cache.put(requestUrl, fetched.clone()).catch((e) => {
|
|
console.warn('Cache put failed:', e)
|
|
})
|
|
response = fetched
|
|
}
|
|
} else {
|
|
const fetched = await fetch(requestUrl)
|
|
if (!fetched.ok) throw new Error(`HTTP ${fetched.status}`)
|
|
response = fetched
|
|
}
|
|
|
|
const blob = await response.blob()
|
|
const objectUrl = URL.createObjectURL(blob)
|
|
objectUrlMap.set(imageId, objectUrl)
|
|
return objectUrl
|
|
}
|
|
|
|
export function revokeImageUrl(imageId: string) {
|
|
const url = objectUrlMap.get(imageId)
|
|
if (url) {
|
|
try {
|
|
URL.revokeObjectURL(url)
|
|
} catch (e) {
|
|
/* ignore */
|
|
}
|
|
objectUrlMap.delete(imageId)
|
|
}
|
|
}
|
|
|
|
export function revokeAllImageUrls() {
|
|
for (const url of objectUrlMap.values()) {
|
|
try {
|
|
URL.revokeObjectURL(url)
|
|
} catch (e) {
|
|
/* ignore */
|
|
}
|
|
}
|
|
objectUrlMap.clear()
|
|
}
|