RX
Processing
Sys_Kernel_V1.6.0 // Loading
System: Blog_Engine_v2.0
FIG-2025-558

图床配置指南

如何在 RefactX v1.6中使用图床以及在Waline 评论系统中启用图片上传

Date_Log
December 30, 2025
Data_Size
810 WORDS
Est_Time
6
Categories
#笔记

注意:启用图床将会禁用图片优化功能。如果你强制在./astro.config.mjs的52行处修改为你的图床地址,那大概会被防盗练拦截,所以图片优化仅限静态资源。

首先需要的是S3对象存储,任何提供商理论上都支持,由于涉及到接口的编写,所以以CF R2和Worker举例。

本方法来自 https://tbbbk.com/archives/cloudflare-r2-imgbed 但其中的内容稍有纰漏,所以我决定重写这一部分。

开通对象存储h2

CF主页 > 存储和数据库 > R2对象存储 > 概述 > 创建储存桶

名称为 imgs ,位置选亚太地区,标准类型。

配置Workerh2

CF主页 > 计算和AI > Workers 和 Pages > 创建应用程序

选择从 Hello World! 开始,Worker name填 imgctl ,然后部署。

进入创建好的 Worker ,编辑代码。

Cloudflare worker 配置如下:

你需要在allowedDomains块中引入你的源域名,也就是你需要从哪里访问这些资源。

export default {
async fetch(request, env) {
const url = new URL(request.url);
const pathname = url.pathname;
const referer = request.headers.get('Referer') || '';
const allowedDomains = ['localhost'];
// -----------------------------
// ✅ CORS 预检
// -----------------------------
if (request.method === 'OPTIONS') {
return new Response(null, {
status: 204,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Max-Age': '86400'
}
});
}
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization'
};
// -----------------------------
// ✅ POST 上传(使用 Token 校验)
// -----------------------------
if (request.method === 'POST' && pathname === '/upload') {
try {
const auth = request.headers.get('Authorization');
if (!auth || auth !== `Bearer ${env.UPLOAD_TOKEN}`) {
return new Response(
JSON.stringify({ error: 'Invalid upload token' }),
{ status: 403, headers: { 'Content-Type': 'application/json', ...corsHeaders } }
);
}
const formData = await request.formData();
const file = formData.get('file');
if (!file) {
return new Response(
JSON.stringify({ error: 'No file uploaded' }),
{ status: 400, headers: { 'Content-Type': 'application/json', ...corsHeaders } }
);
}
// ✅ 清理文件名
const ext = file.name.substring(file.name.lastIndexOf('.')) || '';
const filename = `comment/${Date.now()}-${Math.random().toString(36).slice(2)}${ext}`;
const buffer = await file.arrayBuffer();
await env.MY_BUCKET.put(filename, buffer, {
httpMetadata: { contentType: file.type }
});
return new Response(
JSON.stringify({ url: `https://img.refact.cc/${filename}` }),
{ status: 200, headers: { 'Content-Type': 'application/json', ...corsHeaders } }
);
} catch (err) {
return new Response(
JSON.stringify({ error: err.message }),
{ status: 500, headers: { 'Content-Type': 'application/json', ...corsHeaders } }
);
}
}
// -----------------------------
// ✅ GET 图片(Referer 防盗链)
// -----------------------------
if (request.method === 'GET') {
// 只对 GET 做防盗链
let allowed = false;
for (const domain of allowedDomains) {
if (referer.includes(domain)) {
allowed = true;
break;
}
}
if (!allowed) {
const warning = await env.MY_BUCKET.get('hotlink-warning.png');
if (warning) {
return new Response(warning.body, {
status: 200,
headers: {
'Content-Type': 'image/png',
'Cache-Control': 'public, max-age=3600',
'X-Robots-Tag': 'noindex'
}
});
}
return new Response('Hotlinking not allowed', {
status: 403,
headers: { 'Content-Type': 'text/plain', 'X-Robots-Tag': 'noindex' }
});
}
const key = pathname.slice(1);
if (!key) return new Response('Bad Request', { status: 400 });
const object = await env.MY_BUCKET.get(key);
if (!object) return new Response('Not Found', { status: 404 });
const headers = new Headers();
object.writeHttpMetadata(headers);
headers.set('etag', object.httpEtag);
headers.set('Cache-Control', 'public, max-age=31536000, immutable');
headers.set('Access-Control-Allow-Origin', '*');
return new Response(object.body, { status: 200, headers });
}
return new Response('Method Not Allowed', { status: 405 });
}
};

接下来绑定储存桶,添加绑定。选择R2 储存桶。变量名称必须填 MY_BUCKET 。R2 储存桶选imgs 。

下一步绑定域名,设置 > 域和路由,你需要添加一个自定义域,方便在文章中引入。

转到设置 > 变量和机密,你还需要加入一个名为UPLOAD_TOKEN的环境变量

变量名UPLOAD_TOKEN
自行设置,复杂一些,如果不使用Waline图片上传,可以忽略这一步。

配置PicGo或PicListh3

在这两款软件中,驱动选S3对象存储就可以。

CF主页 > 计算和AI > Workers 和 Pages > 概述

在Account detials 中 API Token栏右侧的 Manage按钮,创建 Account API 令牌,名称自拟,权限是对象读和写,其他保持默认。

复制Access Key ID 和 Secret Access Key(只显示一次),桶名就填imgs, 设定自定义节点,就填创建令牌时显示的S3 API。概述页最底部显示的内容。你还可以自定义上传路径。其他配置保持不变。

本文描述相对简洁,如果有疑问可以去看原文,如果问题是由本项目导致的,请提交issues。

在Waline 评论系统中启用图片上传h2

在./src/config.ts 配置文件的末尾,是有关waline评论系统的配置项,其中新增了三个配置项:是否启用图片上传功能、Waline 图片上传地址和Waline 上传 Token。这三个选项必须同时设置。

Waline评论上传的图片默认统一保存在comment文件夹下。

根据Worker的默认配置,图片上传地址应为https://img.example.com/upload 注意不要填错了。至此,配置完成,你在写文章时,通过PicGo/PicList上传至数据库,然后在Markdown文件中按照和以前一样的格式从图床引用就好。

// 带描述的图片
:::image-figure[lighthouse]
![](https://img.refact.cc/lighthouse.png)(style: width:600px;)
:::

更多用法,请到 https://refact-x-template.vercel.app/posts/markdown-extension-syntax 学习。

外部访问提示h2

你可以在存储桶的根目录放置一张提示图,命名为hotlink-warning.png,大概就是提示禁止从外部访问,不放也会有相应的文字提示。

12月26日 起稿

12月30日 初稿

1月4日 第一次修改

// USER_FEEDBACK // DATA_STREAM
LIVE_LINK

Comments_

Initializing