本文讲解如何使用 Cloudflare Pages+Telegraph 图床仓库,实现图床图片在浏览器中直接显示,而不是被默认下载。
写给有一定 Cloudflare Worker/Pages 基础的用户。

背景 | 原理

Telegraph 图床部署使用方便,但直接访问图片链接时,浏览器默认直接下载而不是在浏览器中显示。原因:

  • Telegram API 返回的 Content-Type 有时是 application/octet-stream
  • Cloudflare Pages 部署时可能也会影响 Content-Disposition。

我们希望实现:

  1. 图片直接显示(jpg、png、gif、webp、bmp,可扩展)。
  2. 视频、HTML、JS 等非图片文件保持下载。

因此我们可以把图片的 HTTP 响应头改成浏览器能认的形式,让浏览器直接显示而不是下载。
如下:

1
2
newHeaders.set("Content-Disposition", "inline"); // 强制浏览器显示图片
newHeaders.set("Content-Type", "image/jpeg"); // 修正 Telegram 返回的二进制类型

这样修改后,浏览器就能识别这是图片,并且按 inline 渲染,而不是下载。


Fork 仓库

  1. 打开官方 Telegraph-Image 仓库:
    👉 https://github.com/cf-pages/Telegraph-Image

  2. 点击右上角 Fork,将仓库 Fork 到你自己的 GitHub 账户。

  3. 相关Github操作不再赘述


部署到 Cloudflare Pages

具体操作引自linux.do网站的帖子,自行前往查看:
👉 https://linux.do/t/topic/798069

[id].js 文件的修改

根据最新的Telegraph-Image项目结构,你需要在functions/file目录下找到 [id].js 文件,点击后切换到 Editor 编辑模式来编写代码

图片
图片

ctrl + A 全选代码后删除
下面是修改后的完整 [id].js 文件(对代码结构做了一定优化),将原有代码 全部 替换为以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
export async function onRequest(context) {
const { request, env, params } = context;
const url = new URL(request.url);

let fileUrl = 'https://telegra.ph/' + url.pathname + url.search;

if (url.pathname.length > 39) {
const fileId = url.pathname.split(".")[0].split("/")[2];
const filePath = await getFilePath(env, fileId);
if (filePath) {
fileUrl = `https://api.telegram.org/file/bot${env.TG_Bot_Token}/${filePath}`;
}
}

const response = await fetch(fileUrl, {
method: request.method,
headers: request.headers,
body: request.body,
});

if (!response.ok) return response;

const isAdmin = request.headers.get('Referer')?.includes(`${url.origin}/admin`);
if (isAdmin) return forceImageInline(response, fileUrl);

if (!env.img_url) return forceImageInline(response, fileUrl);

let record = await env.img_url.getWithMetadata(params.id);
if (!record || !record.metadata) {
record = {
metadata: {
ListType: "None",
Label: "None",
TimeStamp: Date.now(),
liked: false,
fileName: params.id,
fileSize: 0,
}
};
await env.img_url.put(params.id, "", { metadata: record.metadata });
}

const metadata = {
ListType: record.metadata.ListType || "None",
Label: record.metadata.Label || "None",
TimeStamp: record.metadata.TimeStamp || Date.now(),
liked: record.metadata.liked !== undefined ? record.metadata.liked : false,
fileName: record.metadata.fileName || params.id,
fileSize: record.metadata.fileSize || 0,
};

if (metadata.ListType === "White") {
return forceImageInline(response, fileUrl);
}

if (metadata.ListType === "Block" || metadata.Label === "adult") {
const referer = request.headers.get('Referer');
const redirectUrl = referer
? "https://static-res.pages.dev/teleimage/img-block-compressed.png"
: `${url.origin}/block-img.html`;
return Response.redirect(redirectUrl, 302);
}

if (env.WhiteList_Mode === "true") {
return Response.redirect(`${url.origin}/whitelist-on.html`, 302);
}

if (env.ModerateContentApiKey) {
try {
const moderateUrl = `https://api.moderatecontent.com/moderate/?key=${env.ModerateContentApiKey}&url=https://telegra.ph${url.pathname}${url.search}`;
const moderateResponse = await fetch(moderateUrl);
if (moderateResponse.ok) {
const moderateData = await moderateResponse.json();
if (moderateData?.rating_label) {
metadata.Label = moderateData.rating_label;
if (moderateData.rating_label === "adult") {
await env.img_url.put(params.id, "", { metadata });
return Response.redirect(`${url.origin}/block-img.html`, 302);
}
}
}
} catch (error) {
console.error("Moderation error:", error.message);
}
}

await env.img_url.put(params.id, "", { metadata });

// 强制图片 inline 返回
return forceImageInline(response, fileUrl);
}


// 强制图片显示,其他文件默认浏览器处理
function forceImageInline(originalResponse, fileUrl) {
const newHeaders = new Headers(originalResponse.headers);

// 根据文件后缀判断是否图片
const lower = fileUrl.toLowerCase();
if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) newHeaders.set("Content-Type", "image/jpeg");
else if (lower.endsWith(".png")) newHeaders.set("Content-Type", "image/png");
else if (lower.endsWith(".gif")) newHeaders.set("Content-Type", "image/gif");
else if (lower.endsWith(".webp")) newHeaders.set("Content-Type", "image/webp");
else if (lower.endsWith(".bmp")) newHeaders.set("Content-Type", "image/bmp");

// 只针对图片强制 inline
if (newHeaders.get("Content-Type")?.startsWith("image/")) {
newHeaders.set("Content-Disposition", "inline");
}

return new Response(originalResponse.body, {
status: originalResponse.status,
statusText: originalResponse.statusText,
headers: newHeaders
});
}


async function getFilePath(env, file_id) {
try {
const res = await fetch(`https://api.telegram.org/bot${env.TG_Bot_Token}/getFile?file_id=${file_id}`, { method: 'GET' });
if (!res.ok) return null;
const data = await res.json();
if (data.ok && data.result) return data.result.file_path;
return null;
} catch (error) {
console.error("getFilePath error:", error.message);
return null;
}
}

完成后保存

图片
图片

接下来,Cloudflare Pages会自动同步Github上的部署更新,耐心等待即可

视频 / HTML / JS 文件保持默认下载,不会影响浏览器显示。


效果展示:
图片
图片


常见问题

  • 图片仍下载:检查 Cloudflare、Github 是否部署成功,确保文件后缀是图片类型。
  • 视频下载正常:这是设计行为,只强制图片 inline。
  • 白名单 / 黑名单无效:KV 是否绑定成功,metadata 是否存在。

小结

这篇文章旨在解决telegraph搭建的图床,在上传图片后在浏览器打开直接下载,而不是浏览器直接显示的问题。
希望对你有帮助么么么么么么叽~~

(牢骚:Math is difficult for me T____T)(doge

Made by 可达鸭战神