ow
This commit is contained in:
1 parent
70087c57cd
commit
70b3e19798
1 file changed
+18
-16
@@ -1,39 +1,41 @@
|
|||||||
/**
|
/**
|
||||||
* 图片加载失败重试。
|
* 图片加载失败重试(最多 3 次)。
|
||||||
*
|
*
|
||||||
* 背景:视频封面(jpg)是服务端 ffmpeg 异步生成的,列表查询返回时文件常常还没落盘,
|
* 背景:视频封面(jpg)是服务端 ffmpeg 生成的,列表查询返回时文件可能还没落盘,
|
||||||
* 首次请求会拿到 404;而 <img> 一旦失败不会自动重试,只有 src 变了才会再请求一次,
|
* 首次请求会拿到 404;而 <img> 一旦失败不会自动重试,只有 src 变了才会再请求一次,
|
||||||
* 表现就是"必须手动刷新页面才有图"。
|
* 表现就是"必须手动刷新页面才有图"。
|
||||||
*
|
*
|
||||||
* 这里失败后按退避重试,重试时把查询参数换成一次性时间戳绕开缓存。
|
* 计数口径很关键:服务端每次查询都会重新拼 ?v=时间戳,src 每次都不一样。
|
||||||
* 重试次数用 dataset 记在 <img> 上,src 变了(重新查询拿到新地址)就重新计数。
|
* 如果按 src 计数,列表每刷新一次计数就被重置,会一直重试、图片一闪一闪。
|
||||||
|
* 所以这里按「去掉查询参数的图片地址」累计,一个封面最多重试 3 次,用完就放弃。
|
||||||
*/
|
*/
|
||||||
const RETRY_DELAYS = [800, 2000, 4000];
|
const RETRY_DELAYS = [800, 2000, 4000];
|
||||||
|
const MAX_RETRY = RETRY_DELAYS.length;
|
||||||
|
|
||||||
export const retryImage = (e: Event, max: number = RETRY_DELAYS.length) => {
|
/** key = 去掉查询参数的图片地址,value = 已重试次数 */
|
||||||
|
const retryTimes = new Map<string, number>();
|
||||||
|
|
||||||
|
export const retryImage = (e: Event) => {
|
||||||
const img = e.target as HTMLImageElement | null;
|
const img = e.target as HTMLImageElement | null;
|
||||||
if (!img) return;
|
if (!img) return;
|
||||||
|
|
||||||
const src = img.getAttribute("src") || img.src;
|
const src = img.getAttribute("src") || img.src;
|
||||||
if (!src) return;
|
if (!src) return;
|
||||||
|
|
||||||
// 地址变了说明是新一轮数据,允许重新重试
|
// 去掉 ?v= / ?_t= 等一次性参数,保证同一张图只累计一次
|
||||||
if (img.dataset.retrySrc !== src) {
|
const key = src.split("?")[0];
|
||||||
img.dataset.retrySrc = src;
|
|
||||||
img.dataset.retry = "0";
|
|
||||||
}
|
|
||||||
|
|
||||||
const times = Number(img.dataset.retry ?? 0);
|
const times = retryTimes.get(key) ?? 0;
|
||||||
if (times >= max) {
|
if (times >= MAX_RETRY) {
|
||||||
// 彻底拿不到就隐藏图片,露出底下的占位/播放图标,避免裂图
|
// 重试次数用完:隐藏图片露出底下的占位/播放图标,不再尝试,避免反复闪
|
||||||
img.style.visibility = "hidden";
|
img.style.visibility = "hidden";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
img.dataset.retry = String(times + 1);
|
retryTimes.set(key, times + 1);
|
||||||
const base = src.split("?")[0];
|
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
img.src = `${base}?_t=${Date.now()}`;
|
img.src = `${key}?_t=${Date.now()}`;
|
||||||
img.style.visibility = "";
|
img.style.visibility = "";
|
||||||
}, RETRY_DELAYS[Math.min(times, RETRY_DELAYS.length - 1)]);
|
}, RETRY_DELAYS[Math.min(times, RETRY_DELAYS.length - 1)]);
|
||||||
};
|
};
|
||||||
Reference in new issue
Block a user