diff --git a/functions/file/[[path]].js b/functions/file/[[path]].js index da2fe8aa..d350d67a 100644 --- a/functions/file/[[path]].js +++ b/functions/file/[[path]].js @@ -175,7 +175,9 @@ async function handleTelegramChunkedFile(context, imgRecord, encodedFileName, fi const headers = new Headers(); setCommonHeaders(headers, encodedFileName, fileType, Referer, url); headers.set('Content-Length', totalSize.toString()); - + + headers.append('Vary', 'Range'); + // 添加ETag支持 const etag = `"${metadata.TimeStamp || Date.now()}-${totalSize}"`; headers.set('ETag', etag); @@ -268,20 +270,27 @@ async function handleTelegramChunkedFile(context, imgRecord, encodedFileName, fi } }); - // 设置Range相关头部 - if (isRangeRequest) { - setRangeHeaders(headers, rangeStart, rangeEnd, totalSize); - - return new Response(stream, { - status: 206, // Partial Content - headers, - }); - } else { - return new Response(stream, { - status: 200, - headers, - }); - } +// 设置Range相关头部 +if (isRangeRequest) { +setRangeHeaders(headers, rangeStart, rangeEnd, totalSize); + + // 明确告诉浏览器/CF:支持分段请求 + headers.set('Accept-Ranges', 'bytes'); + + return new Response(stream, { + status: 206, // Partial Content + headers, + }); +} else { + // 关键:避免首个 200 整段被缓存,导致后续 Range 请求直接命中 200 + headers.set('Cache-Control', 'no-store'); // 或 'max-age=0, must-revalidate' + + return new Response(stream, { + status: 200, + headers, + }); +} + } catch (error) { return new Response(`Error: Failed to reconstruct chunked file - ${error.message}`, { status: 500 }); diff --git a/functions/file/fileTools.js b/functions/file/fileTools.js index 1f8a5ead..52ddff01 100644 --- a/functions/file/fileTools.js +++ b/functions/file/fileTools.js @@ -35,6 +35,8 @@ export function setCommonHeaders(headers, encodedFileName, fileType, Referer, ur headers.set('Content-Disposition', `inline; filename="${encodedFileName}"; filename*=UTF-8''${encodedFileName}`); headers.set('Access-Control-Allow-Origin', '*'); headers.set('Accept-Ranges', 'bytes'); + + headers.append('Vary', 'Range'); if (fileType) { headers.set('Content-Type', fileType); @@ -81,25 +83,57 @@ export async function getFileContent(request, targetUrl, max_retries = 2) { let retries = 0; while (retries <= max_retries) { try { - const response = await fetch(targetUrl, { - method: request.method, - headers: request.headers, - body: request.body, + // 复制请求头,确保 Range / If-None-Match 等关键头被透传 + const fwdHeaders = new Headers(request.headers); + + // 显式兜底:有些运行时需要手动 set 一下 + const range = request.headers.get("Range"); + if (range) fwdHeaders.set("Range", range); + + // 只允许 GET/HEAD 转发 + const method = request.method === "HEAD" ? "HEAD" : "GET"; + + const upstreamReq = new Request(targetUrl, { + method, + headers: fwdHeaders, + redirect: "follow", }); - if (response.ok || response.status === 304) { - return response; - } else if (response.status === 404) { - return new Response('Error: Image Not Found', { status: 404 }); + + const upstreamRes = await fetch(upstreamReq); + + if (upstreamRes.ok || upstreamRes.status === 304) { + const headers = new Headers(upstreamRes.headers); + + // 关键:让缓存区分是否带 Range + headers.append("Vary", "Range"); + + // 如果是 Range 请求但上游仍返回 200,这里兜底改成 206 + if (range && upstreamRes.status === 200) { + headers.set("Accept-Ranges", "bytes"); + return new Response(upstreamRes.body, { + status: 206, + headers, + }); + } + + return new Response(upstreamRes.body, { + status: upstreamRes.status, + statusText: upstreamRes.statusText, + headers, + }); + } else if (upstreamRes.status === 404) { + return new Response("Error: Image Not Found", { status: 404 }); } else { retries++; } - } catch (error) { + } catch (_err) { retries++; } } return null; } + export function isTgChannel(imgRecord) { return imgRecord.metadata?.Channel === 'Telegram' || imgRecord.metadata?.Channel === 'TelegramNew'; } @@ -208,4 +242,4 @@ export async function returnWhiteListImg(url) { }, }); } -} \ No newline at end of file +}