Vercel 留言板搭建与部署完整教程(Vercel Blob + Redis 版)
图片存 Vercel Blob CDN,永久保存、全球加速;留言存 Upstash Redis,冷启动不丢数据。
一、功能特性
- ✅ 发布留言(昵称 + 内容 + 表情 + 图片 + 头像)
- ✅ 自定义头像上传(永久保留,下次留言自动使用)
- ✅ 图片上传 → Vercel Blob CDN(永久保存,全球加速读取)
- ✅ 留言存储 → Upstash Redis(冷启动不丢数据)
- ✅ 6 大类表情选择器(300+ 表情)
- ✅ 响应式设计(手机/电脑都能用)
- ✅ 自动刷新(30 秒轮询)
- ✅ 管理员删除留言
二、架构说明
1 | ┌─────────────────────────────────────────────┐ |
为什么用 Vercel Blob 存图片?
- 图片存 CDN,全球边缘节点加速,加载速度快
- 永久保存,不设过期时间
- 免费 1GB 存储空间
- 图片大小限制 4MB(比 Redis 方案的 1MB 更大)
为什么需要图片代理 /api/image?
- Vercel Blob 默认返回
Content-Disposition: attachment,浏览器会下载而不是显示 - 加一个代理接口,强制返回
Content-Disposition: inline,图片直接在浏览器显示 - 代理接口缓存 1 天,不影响性能
为什么用 Upstash Redis 存留言?
- Vercel KV 已迁移到 Marketplace,无法通过 API Token 创建
- Upstash Redis 通过 Vercel Marketplace 一键安装,环境变量自动注入
- 免费额度:每天 10,000 次命令,256MB 存储
- 留言数据量小,完全够用
三、项目结构
1 | guestbook-blob/ |
与 Redis 方案的区别:
- Redis 方案:
api/upload.js+api/image.js(图片存 Redis,通过 API 读取) - Blob 方案:
api/upload.js+api/image.js(图片存 Blob,通过代理读取,强制 inline 显示)
四、Vercel 配置步骤(详细图文)
4.1 创建 Vercel 项目
- 登录 vercel.com/dashboard
- 点击 Add New… → Project
- 导入 Git 仓库(或后续用 CLI 部署)
4.2 创建 Upstash Redis 数据库
- 进入项目 → Storage 标签
- 找到 Upstash → 点 Browse
- 跳转到 Upstash 页面,选择 Upstash for Redis
- 选择安装计划:
- Pay As You Go — 有免费额度(每天 10,000 次命令,256MB 存储)
- 不需要付款,留言板完全免费
- 创建 Redis 数据库:
- 名称:
guestbook-kv - 区域:选 Asia Pacific (Tokyo) 或最近的区域
- 点 Create
- 名称:
- 回到 Vercel Dashboard → Storage → 找到刚创建的 Redis
- 点 Connect to Project → 选你的项目 → 勾选全部环境 → Connect
注入的环境变量:
1 | KV_REST_API_URL = https://xxx.upstash.io |
4.3 创建 Vercel Blob 存储
- 回到项目 → Storage 标签
- 找到 Blob → 点 Create Database
- 名称:
guestbook-blob-store - 点 Create
- 创建后 → Connect to Project → 选你的项目 → 勾选全部环境 → Connect
注入的环境变量:
1 | BLOB_READ_WRITE_TOKEN = vercel_blob_rw_xxx |
4.4 验证环境变量
进入 Settings → Environment Variables,确认以下 6 个变量存在:
| 变量名 | 用途 |
|---|---|
KV_REST_API_URL |
Redis REST API 地址 |
KV_REST_API_TOKEN |
Redis REST API 令牌 |
KV_REST_API_READ_ONLY_TOKEN |
Redis 只读令牌 |
KV_URL |
Redis 连接地址 |
REDIS_URL |
Redis 连接地址 |
BLOB_READ_WRITE_TOKEN |
Blob 读写令牌 |
五、完整源码
5.1 package.json
1 | { |
5.2 api/messages.js
1 | import { Redis } from '@upstash/redis'; |
Redis 操作说明:
redis.lpush(key, value)— 从左侧插入(新留言在前)redis.lrange(key, 0, -1)— 获取全部留言redis.llen(key)— 获取留言总数redis.ltrim(key, 0, N-1)— 只保留前 N 条redis.del(key)— 删除整个列表
5.3 api/upload.js
1 | import { put } from '@vercel/blob'; |
关键点:
put()是@vercel/blob的上传函数access: 'public'设置为公开访问blob.url返回的是 CDN 地址,但默认是attachment(下载),需要通过代理显示
5.4 api/image.js
1 | export default async function handler(req, res) { |
为什么需要这个代理?
- Vercel Blob 默认返回
Content-Disposition: attachment,浏览器会下载 - 代理接口强制返回
Content-Disposition: inline,图片直接在浏览器显示 - 代理接口缓存 1 天(
max-age=86400),不影响性能 - 前端图片 URL 格式:
/api/image?url=${encodeURIComponent(blobUrl)}
5.5 index.html
1 |
|
5.6 style.css
1 | * { margin: 0; padding: 0; box-sizing: border-box; } |
5.7 script.js
1 | // ========== 表情数据 ========== |
六、部署
6.1 CLI 部署
1 | cd guestbook-blob |
6.2 关联 Git 仓库
1 | git init |
七、API 接口
| 接口 | 方法 | 说明 |
|---|---|---|
/api/messages |
GET | 获取所有留言 |
/api/messages |
POST | 发布新留言 |
/api/messages |
DELETE | 删除留言(管理员) |
/api/upload |
POST | 上传图片/头像 → Vercel Blob |
/api/image?url=xxx |
GET | 图片代理 → 强制 inline 显示 |
POST /api/messages 请求体:
1 | { |
POST /api/upload 请求体:
1 | { |
返回:{ "url": "https://xxx.public.blob.vercel-storage.com/guestbook/xxx.jpg" }
注意:Blob 返回的 URL 是 CDN 地址,前端直接用 <img src="url"> 显示,无需额外的读取接口。
八、免费额度
| 服务 | 免费额度 | 说明 |
|---|---|---|
| Upstash Redis | 10,000 次/天 | 读写都算 |
| Upstash Redis | 256MB 存储 | 约可存 5 万条留言 |
| Vercel Blob | 1GB 存储 | 约可存 2000-10000 张图片 |
| Vercel Blob | 无限带宽 | CDN 全球加速 |
九、常见问题
Q: Blob 上传报错 “No token found”?
A: 确认 BLOB_READ_WRITE_TOKEN 已注入。在 Vercel Dashboard → Storage → Blob → Connect to Project。
Q: Redis 连接失败?
A: 确认 KV_REST_API_URL 和 KV_REST_API_TOKEN 已注入。在 Vercel Dashboard → Storage → Upstash → Connect to Project。
Q: 图片上传报错 “不能超过 4MB”?
A: Vercel Blob 限制单文件 4MB。压缩图片后再上传。
Q: 点击图片为什么是下载而不是显示?
A: Vercel Blob 默认返回 Content-Disposition: attachment(下载)。解决方法:
- 创建
api/image.js代理接口,强制返回Content-Disposition: inline - 前端图片 URL 格式:
/api/image?url=${encodeURIComponent(blobUrl)} - 代理接口缓存 1 天,不影响性能
Q: 图片会过期吗?
A: 不会。Vercel Blob 图片永久保存(除非手动删除)。
Q: 冷启动后数据还在吗?
A: 在。留言存在 Upstash Redis 云端,图片存在 Vercel Blob CDN。
Q: 如何查看 Redis 中的数据?
A: Upstash Dashboard → Data Browser。
Q: 如何查看 Blob 中的图片?
A: Vercel Dashboard → Storage → Blob → 文件列表。
Q: 如何清空所有留言?
A: 在 Upstash Data Browser 中删除 guestbook-blob:messages key。
Q: 两个版本有什么区别?
| Redis 版 | Blob 版 | |
|---|---|---|
| 图片存储 | Redis (base64) | Vercel Blob (CDN) |
| 图片大小 | 1MB | 4MB |
| 图片过期 | 90 天 | 永久 |
| 读取速度 | 经过 API | CDN 直读 |
| 依赖 | @upstash/redis | @upstash/redis + @vercel/blob |
| 配置 | 1 个数据库 | 1 个数据库 + 1 个 Blob |
| 留言的文本,图片保存在哪个路径 |
────────────────────────────────────────
╭─ ⚕ Hermes ───────────────────────────────────────────────────────────────────╮
这个项目的文本和图片都没有保存到磁盘。
具体来说:
- 留言文本:保存在
api/messages.js的内存变量let messages = []里,Vercel 函数冷启动后会清空 - 图片:在
api/upload.js里转成 base64 data URL,直接嵌入留言对象中,也在内存里
这是 Serverless 架构的特点——没有持久化文件系统。
如果需要持久保存,有两个方案:
方案一:接入 Vercel KV(Redis)
- Vercel Dashboard → Storage → Create KV Database
- 文本用
kv.lpush()存,图片用 Vercel Blob 存
方案二:接入 Supabase(免费 PostgreSQL)
- 创建 messages 表存文本
- 用 Supabase Storage 存图片文件
需要我帮你改成带数据库的版本吗?
代码已更新部署完成。现在需要你在 Vercel Dashboard 配置 Upstash Redis:
- 打开 https://vercel.com/5fans-projects/guestbook/storage
- 找到 Upstash → 点 Browse 或 Install
- 会跳转到 Upstash 页面,创建一个 Redis 数据库:
- 名称:
guestbook-kv - 区域:选 Asia Pacific (Tokyo) 或最近的区域
- 点 Create
- 名称:
- 创建完后回到 Vercel Dashboard → Storage → 找到刚创建的 Redis 数据库
- 点 Connect to Project → 选 guestbook → 勾选全部环境 → Connect
完成后告诉我,环境变量会自动注入。
换个方案。放弃 Vercel Blob,图片直接存 Redis(base64),不需要额外配置:
总结详细的创建编辑部署教程并制作成.md文件放在/Users/apple/Downloads/blog/8video
,music\ box,Gitalk\ ,genxiangce,spring,guajian/source/_posts位置
╭─ ⚕ Hermes ───────────────────────────────────────────────────────────────────╮
图片接口正常,200 OK,返回 293KB 的 webp 图片。
完整地址:
https://guestbook-phi-lilac.vercel.app/api/image?id=img_moq5xs6y.webp
这是前端留言列表里自动显示的,不需要你手动拼。用户上传图片后,script.js 会自动用这个 URL 显示图片。
你可以打开 https://guestbook-phi-lilac.vercel.app 看看效果,那条”小白”的留言应该能看到图片。
● 文字的完整地址有吗
────────────────────────────────────────
╭─ ⚕ Hermes ───────────────────────────────────────────────────────────────────╮
有:
https://guestbook-phi-lilac.vercel.app/api/messages
,
Vercel Blob和Redis 存储图片的区别
────────────────────────────────────────
╭─ ⚕ Hermes ───────────────────────────────────────────────────────────────────╮
Vercel Blob:
- 专门存文件的 CDN,图片直接 URL 访问,速度快
- 1GB 免费存储,无限带宽
- 文件永久保存(除非手动删)
- 缺点:BLOB_READ_WRITE_TOKEN 需手动在 Dashboard 配置,API Token 无法自动创建
**Redis (Upstash)**:
- 通用键值数据库,存 base64 字符串,需要通过
/api/image接口读取 - 256MB 免费存储,10,000 次/天
- 可设过期时间(我设了 90 天)
- 缺点:每次读图片都要经过 Serverless Function,比 CDN 慢;base64 编码比原文件大 33%
简单说:
- Blob = 专门存图片,快,省空间
- Redis = 通用数据库存图片,方便但慢一点
对留言板这种小场景,Redis 完全够用。图片少、访问量小,体验差别不大。
● 帮我再新建一个留言板,要求用Vercel Blob
第二个留言板
https://guestbook-blob.vercel.app
项目已创建并部署:https://guestbook-blob.vercel.app
现在需要连接数据库。让我检查现有的 Upstash Redis:
环境变量是加密的,不能直接复制。需要在 Dashboard 操作:
- 打开 https://vercel.com/5fans-projects/guestbook-blob/storage
- 找到 Upstash → 点 Browse
- 找到你之前创建的 Redis 数据库 → 点 Connect to Project
- 选 guestbook-blob → 勾选全部环境 → Connect
然后还需要配置 Vercel Blob:
- 回到 Storage 页面
- 找到 Blob → 点 Create Database
- 名称:
guestbook-blob-store - 点 Create
- 创建后 → Connect to Project → 选 guestbook-blob → 全部环境 → Connect
完成后告诉我,我帮你重新部署。
╭─ ⚕ Hermes ───────────────────────────────────────────────────────────────────╮
看到 BLOB_READ_WRITE_TOKEN 已经注入了。但 Upstash Redis 的环境变量还没有。让我先重新部署让 Blob 生效:
╰────────────────────────
Blob 已经连上了,但 Upstash Redis 还没连。
你需要在 Dashboard 操作:
- 打开 https://vercel.com/5fans-projects/guestbook-blob/storage
- 找到 Upstash → 点 Browse
- 找到你之前创建的 Redis 数据库(guestbook-kv)→ 点 Connect to Project
- 选 guestbook-blob → 勾选全部环境 → Connect
完成后告诉我。
╭─ ⚕ Hermes ───────────────────────────────────────────────────────────────────╮
点 Upstash for Redis。
然后会跳转到 Upstash 页面,找到你之前创建的那个 Redis 数据库(guestbook-kv),点 Manage → Connect to Project → 选 guestbook-blob → 勾选全部环境 → Connect。
选 Pay As You Go,不需要付款。
这个有免费额度:每天 10,000 次命令,留言板够用了。超出才收费。
就是 Pay As You Go。
它有免费额度:每天 10,000 次命令,256MB 存储。留言板完全免费。
/guestbook/moq7s9gl-X8MvPJ8B7s2BR2Fzn3tF610sKzn8Oq.png
https://ejndelrzy8sjglav.public.blob.vercel-storage.com/guestbook/moq7s9gl-X8MvP
J8B7s2BR2Fzn3tF610sKzn8Oq.png 点击了显示要下载吗?点击了可以打开图片吗
────────────────────────────────────────
┊ 💻 preparing terminal…
╭─ ⚕ Hermes ───────────────────────────────────────────────────────────────────╮
找到原因了!Vercel Blob 默认返回 Content-Disposition: attachment,浏览器会下载而不是显示。
修改 api/upload.js,上传时加 contentDisposition: 'inline':
之前上传的图片还是 attachment,需要重新上传一次才会变成 inline。你可以去留言板 https://guestbook-blob.vercel.app 重新上传一张图片测试。
旧图片还是 attachment。需要重新上传一张新图测试。让我直接用 API 上传测试:
╰────────────────────────────────────────────────────────────contentDisposition: 'inline' 对 Vercel Blob 不起作用。换个思路——加个代理接口,强制返回 inline: 这个就可以了,
╰─────────────────────
╭─ ⚕ Hermes ───────────────────────────────────────────────────────────────────╮
代理生效了。现在打开图片会直接在浏览器显示,不会下载。
打开看看,应该直接显示图片了。
https://console.upstash.com/vercel/kv/d29d53b6-6fc3-4c4d-9976-436b1703cb82/data-
browser 这里的图片,打开 “imageUrl”:
“https://ejndelrzy8sjglav.public.blob.vercel-storage.com/guestbook/moq7s9gl-X8Mv
PJ8B7s2BR2Fzn3tF610sKzn8Oq.png”,没有直接在浏览器显示,而是跟之前的一样出现下载。
────────────────────────────────────────
╭─ ⚕ Hermes ───────────────────────────────────────────────────────────────────╮
这是正常的。
Redis 里存的是 原始 Blob URL,点击它直接访问 Blob,所以还是下载。
前端显示已经修好了——script.js 会把 URL 包一层代理:
1 | /api/image?url=https://ejndelrzy8sjglav.public.blob.vercel-storage.com/... |
所以:
- 在 Upstash Data Browser 点 URL → 下载(原始 Blob URL)
- 在留言板页面看图片 → 直接显示(走代理)
这是两个不同的入口,代理只在前端生效。你在留言板 https://guestbook-blob.vercel.app 看图片应该是正常显示的。
在 Vercel 添加自定义域名:
- 打开 https://vercel.com/5fans-projects/guestbook-blob/settings/domains
- 输入你的域名(比如
guestbook.5fan.top) - 点 Add
- 按提示配置 DNS:
- 方式一:CNAME 记录 →
cname.vercel-dns.com - 方式二:A 记录 →
76.76.21.21
- 方式一:CNAME 记录 →
如果你用的是 Cloudflare:
- DNS → 添加 CNAME → 名称
guestbook→ 目标cname.vercel-dns.com→ 代理关闭(灰色云朵)
添加后等 1-2 分钟生效,Vercel 会自动配置 SSL 证书。
教程作者:彭金涛 | 最后更新:2026-05-04
