市面上的图床要么纯存储没展示,要么相册好看但不能外链。我想要两者兼得——于是自己写了一个。

缘起:图床和相册为什么不能是一个东西?

用过不少图床服务,最后都遇到了同一个尴尬:

  • 纯图床(各种 S3 直链服务):上传快、外链稳,但给别人看图时只能甩一坨裸链接,没有相册、没有排版、没有地图轨迹。
  • 相册站(各种开源相册程序):展示效果漂亮,瀑布流、EXIF 面板、标签搜索一应俱全——但拿外链?要么没有这个功能,要么链接长得像乱码。

我想要的东西其实很简单:

  1. 当图床用:上传后一键复制 Markdown / HTML / 纯 URL / BBCode 外链,贴到博客、论坛直接显示。
  2. 当相册用:访客打开网站能看到排版精美的相册,支持标签、搜索、图片详情、EXIF 信息。
  3. 再加点料:实况照片(Live Photo)能动起来,带 GPS 的照片在 3D 地球上标出足迹。

听起来不难,但找了一圈——真没有同时满足这三条的现成方案。

那就自己写。LandCloud 就这么来了。


它能做什么

一句话概括:个人图床 + 相册展示网站,对外展示,对内管理,外链、分享、地图一把梭。

核心能力一览

能力说明
图片外链直链(CDN)+ 代理链接(站内 /f/ 路由),Markdown/HTML/URL/BBCode 四种格式一键复制
相册展示公开 / 私密 / 密码三种可见性;瀑布流网格、响应式分页、浏览量统计
图片处理sharp 自动生成缩略图 + WebP 压缩 + EXIF 提取(相机、镜头、光圈、快门、ISO、GPS)
实况照片支持 Apple / Google / Samsung / 小米 / OPPO / vivo 的 Live Photo,前端自动识别并播放
HEIC 转换iPhone 拍的 HEIC/HEIF 格式自动转 JPEG,变体照常生成
3D 地图足迹Cesium 3D 地球,带 GPS 的照片聚合打点,缩放展开缩略图,点击跳详情
手机扫码上传后台生成二维码,手机扫码免登录上传(HMAC 临时令牌,10 分钟有效)
密码分享自定义密码 + 有效期(永久/1天/7天/30天),bcrypt 哈希存储,错误限频
标签搜索全站关键词搜索(标题/文件名/描述/标签/年份),标签聚合页
主题切换暗色/亮色模式,HeroUI v3 组件体系
SEOsitemap + robots.txt,私密内容禁止收录

上传流程:直传 S3,服务器不扛流量

很多图床的瓶颈在于:图片先过服务器再转存到存储——带宽和内存全浪费在搬运上。

LandCloud 用的是 S3 预签名直传方案:

浏览器请求预签名 URL(校验管理员)
       │
       ▼
浏览器直传 S3  ←─── 流量不过服务器
       │
       ▼
浏览器回调 /api/upload/complete
       │
       ▼
服务端校验对象 → 写记录(pending) → 异步处理
       │
       ▼
sharp 生成变体 + EXIF 提取 → done

上传时流量直接走 浏览器 → S3,服务器只负责签发预签名 URL 和处理回调。处理流水线(缩略图、WebP、EXIF)异步执行,失败可一键重试。

支持 Cloudflare R2 / AWS S3 / MinIO 等任何 S3 协议存储,换服务商只需要改环境变量。


实况照片:让图床也能"动"起来

这是我最得意的功能。

市面上的图床几乎没有支持 Live Photo 的——你上传一张实况照片,它就变成一张死图。但实况照片的本质是「静态图 + 短视频」,明明可以保留动态部分。

LandCloud 实现了一套纯原生的多厂商 Live Photo 解析器

厂商解析方式
Apple双文件模式(HEIC + MOV)
GoogleXMP 元数据解析
SamsungXMP 元数据解析
小米XMP 元数据解析
OPPOXMP 元数据解析
vivoXMP 元数据解析
通用兜底ftyp marker 扫描

上传后自动提取静态帧和关联视频,前端展示时带实况照片徽章,点击播放短视频。HEIC 格式还会自动转 JPEG 生成变体——iPhone 用户终于不用先手动转格式了。


3D 地图足迹:你的照片会说话

拍照片带 GPS 坐标的人越来越多,但大多数相册程序对 GPS 数据的利用止步于「在地图上标个点」。

LandCloud 集成了 Cesium 3D 地球

  • 所有含 GPS 的公开照片自动聚合打点
  • 缩放级别变化时聚合数量动态调整
  • 点击聚合点展开该位置的缩略图列表
  • 点击缩略图跳转到图片详情页
  • 侧边统计卡片展示足迹覆盖范围

Cesium 采用路由级按需加载,首页 bundle 不含地图代码——不会拖慢首屏性能。


手机扫码上传:随拍随传

电脑上写博客,手机上拍了张照片想立刻用?不用数据线,不用传到中间服务再下载。

后台点一下「手机上传」,弹出二维码。手机扫码进入上传页(HMAC 临时令牌,10 分钟有效),直接选照片上传到同一个 S3 桶。回到电脑刷新后台,图已经在列表里了。


安全:该锁的都锁了

图床项目最大的风险不是性能,是数据安全。LandCloud 在这方面下了功夫:

措施说明
Supabase RLS所有数据库表默认拒绝,仅管理员 JWT 可写,访客仅读公开数据
密码分享bcrypt 哈希存储,错误尝试限频(同 IP 5 次/分钟)
预签名 URL10 分钟过期,限定 key 前缀与文件大小
代理路由私密图片一律返回 404(不暴露存在性)
MIME 校验服务端回调时校验 MIME 白名单 + 魔数,拒绝可执行文件伪装
service_role 隔离服务端密钥仅限 Route Handler 内部使用,绝不下发到客户端

技术栈

选型为什么选
框架Next.js 16+(App Router, Turbopack)前后端一体化,Route Handlers + Server Actions
UIHeroUI v3 + Tailwind CSS v4组件库成熟,暗色模式原生支持
数据/认证Supabase(Postgres + Auth + RLS)免费额度够用,RLS 开箱即用
对象存储通用 S3 协议(R2 / S3 / MinIO)协议标准,供应商可随时切换
图片处理sharpNode 生态最快的图片处理库,缩略图/WebP/EXIF 一把梭
地图Cesium 3D 地球3D 可视化天花板,路由级按需加载
测试Vitest(43 项单测)+ Playwright(6 条 E2E)关键路径全覆盖
部署VercelNext.js 原生支持,零配置部署

选型原则和 RadCloud 一样:站在巨人的肩膀上,把精力花在体验打磨上。


部署:5 分钟拥有自己的图床

# 1. Clone 仓库
git clone https://github.com/wyml/LandCloud.git
cd LandCloud

# 2. 安装依赖(自动拷贝 Cesium 静态资源)
npm install

# 3. 配置环境变量
cp .env.example .env.local
# 填入 Supabase URL/Key、S3 凭证、管理员邮箱

# 4. 跑起来
npm run dev

部署到 Vercel 的步骤:

  1. 推送仓库并连接 Vercel 项目
  2. 在项目设置中逐项填入环境变量
  3. 首次部署后在 Supabase 执行 supabase/migrations/ 全部脚本
  4. 冒烟测试:前台首页 → 后台登录 → 上传一张图 → 确认公开页出现 → 私密图直链返回 404

详细的环境变量说明和备份恢复指引都在 README 里。


项目状态

目前 M0~M8 全部完成并提交,M9(测试加固与上线)为收尾里程碑,包含 43 项单元测试和 6 条 Playwright E2E 关键路径。核心功能已全部可用,生产环境部署冒烟通过。

项目完全开源,仓库地址:github.com/wyml/LandCloud


写在最后

从 CATIA 建模到无人机飞控,从医学影像平台到图床相册——看起来跨度很大,但底层逻辑一直没变:遇到不顺手的工具,就自己造一个。

市面上的图床要么没有展示能力,相册要么没有外链能力。LandCloud 把两者揉到一起,还塞进了实况照片和 3D 地球。如果你也想要一个「既是图床又是相册」的个人图片站,欢迎来用、来提意见、来一起参与。

仓库地址:github.com/wyml/LandCloud

有问题或想法,欢迎 Issue 和 PR。


#LandCloud #图床 #相册 #Next.js #Supabase #开源 #Cesium