Skip to content

Latest commit

 

History

97 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

云窝(传图/视频/文件 → 拿链接 · Web 版)

私有图床 + 小型网盘:配合「阿里云 OSS → Cloudflare 免流」教程使用,界面优雅、功能完整:

📖 配套教程:奶昔论坛 · 阿里云 OSS + Cloudflare 免流图床教程NodeSeek · 同教程帖子

  • 存储:阿里云 OSS 私有桶(海外地域,如香港)
  • 分发:Cloudflare 免流域名(你的 2.com),文件访问不经过任何函数,无限流量
  • 上传:浏览器直传 OSS,函数只负责签名、不接触文件内容
    • 小文件(≤10MB):PostObject 表单直传,每传 1 个文件仅调用 1 次函数
    • 大文件(>10MB)分片上传(Multipart)——每片 10MB 独立签名、即签即传,慢网络不限总时长;中断后重选同一文件自动断点续传,失败分片自动重签重试
  • 一套代码,三个平台可跑:腾讯 EdgeOne Makers / 阿里云 ESA Pages + 边缘函数 / Cloudflare Workers & Pages

目录规则(自动归类)

上传时按扩展名自动落到对应目录,前端可切换目录浏览:

目录 内容
upweb/img/ 图片:jpg jpeg png gif webp svg avif bmp ico tiff
upweb/video/ 视频:mp4 webm mov mkv m4v avi flv ts
upweb/other/ 其他一切:压缩包、文档、音频、安装包…

功能

  • 🔒 密码登录门禁(UPLOAD_PASSWORD,≥10 位强制;验证通过换 7 天登录令牌,硬到期不续期,本机不存明文密码;改密码立即作废全部令牌)
  • 🗂️ 任意文件上传:图片 / 视频 / 压缩包 / 文档,拖拽 / 粘贴 / 点选,支持多选批量上传,右下角浮动上传面板实时汇总进度与实时速度(可折叠 / 清空 / 暂停 / 继续 / 取消),大文件长传不挡浏览
  • 🧩 大文件分片上传:>10MB 自动走 OSS Multipart,逐片进度百分比与实时速度;断点续传(进度存本机,中断/刷新/关页面后重选同一文件自动接着传);暂停 / 继续 / 取消(暂停保留断点,取消自动 Abort 清理云端残留分片);分片失败自动重试 3 次并重新签名;ETag 读不到时本地计算分片 MD5 兜底
  • 🗜️ 图片可选智能压缩为 WebP(画质可调,压缩后更大则自动保留原图)
  • ☁️ 云端文件:目录标签页切换(全部 / 图片 / 视频 / 其他)、文件名搜索筛选、分页加载、骨架屏
  • 🎬 灯箱预览:图片看大图、视频在线播放、其他文件显示图标并可打开/下载;复制链接 / Markdown、双次点击确认删除(直接从 OSS 删除);下载走文件直链,IDM 等下载器可自动接管
  • 🔗 上传后一键复制 URL / Markdown
  • 🕘 本机历史记录(localStorage,不上传)
  • 📱 响应式布局,手机可用

目录结构

├── index.html                    # 前端页面(登录门禁/分片上传/断点续传/云端文件/灯箱/历史记录)
├── favicon.png                   # 站点图标
├── functions/api/
│   ├── sign.js                   # 小文件上传签名 + 密码预检(EdgeOne Pages / CF Pages 直接用)
│   ├── list.js                   # 列出桶内文件(支持目录参数)
│   ├── delete.js                 # 删除桶内文件
│   ├── rename.js                 # 重命名文件(同目录 CopyObject + 删除旧 key)
│   └── multipart.js              # 大文件分片上传(init/part签名/complete/abort)
└── adapters/
    ├── cloudflare-workers.js     # Cloudflare Workers 独立版(含全部五个接口)
    └── esa-edge-function.js      # 阿里云 ESA 边缘函数版(含全部五个接口)

准备工作(先完成免流教程)

  1. 按教程建好 OSS 海外私有桶、CF IP 白名单、自定义主机名,确认 https://2.com/某张图 能正常访问
  2. 额外做一步:配置 Bucket 的 CORS(教程里没有,但浏览器直传需要;分片上传必须有 PUT):
    • OSS 控制台 → 你的 Bucket → 数据安全 → 跨域设置 → 创建规则
    • 来源(AllowedOrigin):填你的图床站点域名,如 https://img-admin.edgeone.app(调试期可先用 *
    • 方法(AllowedMethods):POSTPUT⚠️ 老配置只开了 POST,分片上传会失败,务必补上 PUT)
    • 允许的 Header(AllowedHeaders):*
    • 暴露 Header(ExposeHeaders):ETag(建议配;即使不配,前端也会本地计算分片 MD5 兜底,不影响上传)
    • ⚠️ 多条规则时注意:OSS 按「第一条匹配的规则」生效。若存在 * 通配规则排在前面,会抢先匹配你的站点——请给它也加上 ETag 暴露头,或直接删除多余的通配规则
    • ⚠️ 跨域规则生效有最长 15 分钟延迟;改完后若浏览器仍报旧错,用无痕窗口重试(避开预检缓存)
  3. (强烈建议)配置生命周期规则自动清理残留分片:分片上传中断后,已传的分片会暂存 OSS 并计费。OSS 控制台 → 你的 Bucket → 数据管理 → 生命周期 → 创建规则 → 前缀 upweb/ → 「碎片管理」勾选过期碎片 3 天后删除。这样任何烂尾上传都会自动清理,不会悄悄产生存储费
  4. 建议创建 RAM 子账号 AccessKey,直接授权系统策略 AliyunOSSFullAccess(管理对象存储服务 OSS 权限)即可,别用主账号 AK。
    • ⚠️ 不推荐手动勾选最小权限:分片上传的合并请求需要 oss:ListParts,很容易漏配导致合并报 AccessDenied(分片已传成功但 Complete 失败)。已有国内版 OSS 用户踩坑(国际版策略规则可能略有差异),直接给 FullAccess 最省事
    • 如坚持最小化授权,该桶至少需要:oss:PutObjectoss:GetObject(重命名复制源需要)、oss:ListObjectsoss:ListParts(分片合并需要)、oss:DeleteObjectoss:AbortMultipartUpload

环境变量(三平台通用)

变量 示例 说明
OSS_ACCESS_KEY_ID LTAI5t... RAM 子账号 AK
OSS_ACCESS_KEY_SECRET xxxx 对应 SK
OSS_BUCKET my-img Bucket 名
OSS_ENDPOINT oss-cn-hongkong.aliyuncs.com Bucket 概览页底部可查
PUBLIC_URL_BASE https://img.example.com 教程里的访问域名 2.com
UPLOAD_PASSWORD (必填,≥10 位) 上传密码。未配置或少于 10 位时所有接口拒绝服务(500)
ALLOW_ANONYMOUS_UPLOAD (可选) 显式设为 true 才允许免密码公开上传(不推荐,等于把桶开放给全世界)
MAX_SIZE_MB (可选,默认 100) 单文件大小上限;分片上传已突破单请求限制,传大视频可设 500~2000
PART_SIZE_MB (可选,默认 10) 分片大小(MB),范围 5~100。网络差调小(重试代价小),网络好调大(签名次数少)

部署方式一:EdgeOne Makers(推荐)

  1. 把本目录推送到 CNB / GitHub 仓库(环境变量不要写进代码,在控制台配)
  2. EdgeOne Pages → 新建项目 → 导入仓库 → 根目录直接部署(functions/api/*.js 会自动注册为 /api/*
  3. 项目设置 → 环境变量,填上表中的变量
  4. 打开分配的 .edgeone.app 域名即可使用;可绑自定义域名(国内节点需备案)

函数消耗:小文件每传 1 个 = 1 次请求;大文件分片模式 = 每片 1 次签名请求(几 KB),免费额度 300 万次/月,等于用不完。

部署方式二:Cloudflare Workers

  1. Workers 控制台 → Create Worker → 粘贴 adapters/cloudflare-workers.js → Deploy
  2. Settings → Variables and Secrets 配置环境变量
  3. 前端 index.html 部署在 CF Pages(同一仓库时 Pages Functions 可直接用 functions/api/*.js,无需 Worker),或任何静态托管
  4. 若前端和 Worker 不同域,已内置 CORS,无需额外配置

部署方式三:阿里云 ESA

  1. ESA 控制台 → 边缘函数 → 新建 → 粘贴 adapters/esa-edge-function.js → 发布
  2. 若 ESA 支持环境变量则配置变量;不支持就把文件顶部 CONFIG 填好(填完别再提交到公开仓库
  3. 把云盘管理域名关联到该函数;index.html 部署在 ESA Pages 或任意静态托管
  4. 免费版边缘函数 10 万次/天,小文件每个仅 1 次,大文件每片 1 次签名,够用

使用

打开页面 → 先过登录门禁(输入 UPLOAD_PASSWORD 对应的密码)→ 拖文件 / 粘贴图片 / 点选 → 自动归类目录 → 一键复制链接。

💡 想先逛逛界面? 直接把 index.html 部署到任意静态托管(或不配后端环境变量),输入演示密码 demo1234 即可进入纯前端预览模式——界面可完整体验,上传与云端文件需配置后端后生效。

密码验证通过后,服务端会签发一个 7 天有效的登录令牌(HMAC-SHA256 签名),前端此后只保存令牌、不再保存明文密码;7 天内打开自动解锁,到期不续期,回到锁屏重新输入密码即可。

令牌签名密钥是从 OSS SK + Bucket 名 + 密码哈希 派生的(不新增环境变量),带来两个特性:

  • 改密码 = 全部令牌立刻失效:密钥随密码哈希变化,旧令牌验签不过,可直接用于"踢人"
  • 令牌绑定 Bucket:其他部署实例(即使复用同一个 SK)签发的令牌无法登录本站

云端文件:解锁后自动加载 OSS 桶内 upweb/ 下的文件,顶部标签页切换目录(全部/图片/视频/其他),支持平铺/瀑布流/列表三种视图、按时间或大小排序、分页「加载更多」、「刷新」和文件名筛选;点击缩略图打开灯箱——图片看大图、视频直接播放,支持重命名、下载、复制链接/Markdown,其他文件可打开/下载;缩略图底部常驻文件名与大小/日期,灯箱内删除操作独立沉底、需双次确认。

历史记录只存在浏览器 localStorage,不上传、不同步。页脚「退出登录」可清除本机保存的登录令牌。

安全说明:登录门禁只是前端体验层,真正的拦截在函数里——/api/sign/api/multipart/api/list/api/delete/api/rename 都会校验身份(密码或 7 天登录令牌),不对直接 401;分片接口的 key 强制 upweb/ 前缀白名单,且 init 会签发绑定 key/uploadId/声明大小/分片上限/分片尺寸的会话令牌,合并前逐片向 OSS 核验分片号、ETag 与字节数,无法越权操作桶内其他对象、也无法「声明小传大」或「检查后偷换分片」。若未设置 UPLOAD_PASSWORD 或密码少于 10 位,所有接口将拒绝服务(除非显式设置 ALLOW_ANONYMOUS_UPLOAD=true)。

接口一览

接口 方法 说明
/api/sign POST 小文件上传签名(PostObject Policy,bucket + key 精确绑定本次生成的对象键 + 防覆盖条件写入签名);{check:true} 时为身份预检,仅密码验证通过才签发 7 天登录令牌,持令牌预检不续签
/api/multipart POST 大文件分片上传:action=init 初始化(返回 uploadId+key+partSize+会话令牌);action=part 为单个分片签发 1 小时有效的预签名 URL(校验会话令牌,分片号不得超声明上限);action=complete 合并分片(先 ListParts 逐片核验分片号/ETag/字节数与 init 声明完全一致,不符自动 Abort 清理);action=abort 清理残留分片。全程密钥不出服务端
/api/list POST 列出文件,{auth, token?, dir?}(token 为分页游标),dir ∈ all/img/video/other,每页 60 条
/api/delete POST 删除文件,{auth, key},仅允许 upweb/ 前缀
/api/rename POST 重命名文件,{auth, key, name}(新文件名,含扩展名):同目录 CopyObject(x-oss-forbid-overwrite 防覆盖,重名返回 409)+ 删除旧 key,新名字限定 A-Za-z0-9._- 字符集

身份字段说明:所有接口接受 password(明文密码)或 auth(登录令牌)任一;分片接口的 session 为 init 签发的会话令牌。

分片上传与慢网络说明

  • 为什么分片能解决「上传超过 10 分钟」:PostObject 的 Policy 只有 10 分钟有效期,慢网络传大文件可能拖到过期;分片模式下每个分片单独申请预签名 URL(1 小时有效)、即签即传,总上传时长不受任何单一签名限制——哪怕 1GB 文件传一晚上也没问题。
  • 断点续传:已成功分片保存在 OSS 服务端,进度与会话令牌记录在浏览器 localStorage。网络中断、刷新页面、甚至隔天再传——重新选择同一个文件即自动跳过已传分片继续上传。若 OSS 侧会话或本地会话令牌已过期(7 天),前端会自动清掉本地进度重新整个上传。
  • ETag 双保险:合并分片需要每片的 ETag。前端优先读取 OSS 响应头里的 ETag(需 CORS 暴露),读不到时用本地计算的分片 MD5(二者本来相等),因此 CORS 暴露头配不配都能传。
  • 小文件的兜底:≤10MB 文件仍走 PostObject 一次直传;若慢网络拖到 Policy 过期,前端检测到 Policy expired自动重签重传一次
  • 瞬时故障自愈:边缘函数到 OSS 的子请求偶发 net_exception_timeout 类网络抖动。函数侧 init/complete/abort 的 OSS 调用统一带 15s 超时 + 3 次重试;前端对 init/complete 再做一轮重试(part 分片本就有 3 次重签重传)。合并接口额外做了幂等处理——「合并成功但响应丢失」时重试会收到 OSS 的 NoSuchUpload,此时检测到对象已存在即按成功返回,不会触发整包重传;abort 同理,会话已不存在视为清理成功。
  • ⚠️ 同名存在性检查用 GET + Range: bytes=0-0(init 预检与 complete 幂等检查均是)。这个环节踩过两个坑:① GET ?objectMeta 的 200 响应头 Content-Length 是对象大小却没有响应体,EO/ESA 边缘运行时的 fetch 会干等 body 直至平台超时(表现为同名文件上传卡死后报 net_exception_timeout);② HEAD(HeadObject)在本链路返回 403 且错误按 HTTP 语义无响应体,拿不到 OSS 错误码。Range 请求所有状态都有响应体(存在→206、不存在→404、0 字节对象→416 同样证明存在),且 Range 是普通请求头不参与 V1 签名,三个平台运行时都验证可用。
  • 阈值:前端固定以 10MB 为界选择两条通道,与服务端默认 PART_SIZE_MB=10 对齐。

费用说明

  • 函数:三平台免费额度内,小文件 1 次调用/个、大文件每片 1 次签名调用,可忽略
  • 文件访问流量:走 CF 带宽联盟回源免流 + CF 免费 CDN,不产生费用
  • 唯一固定成本:OSS 存储费(海外标准存储,每月每 GB 约 0.1 元人民币量级)
  • ⚠️ 带宽联盟政策要求源站为非中国大陆地域的标准型存储桶;建议用阿里云国际版账号(含每月 1 亿次免费请求等权益),上车后先小额实测一张账单确认
  • ⚠️ OSS 是后付费:保持账户有少量余额 + 开通余额告警,避免欠费停服导致数据进入释放倒计时

安全提示

  • AccessKey 只存于服务端环境变量,前端不可见;令牌签名密钥由 OSS SK + Bucket 名 + 密码哈希 派生,不新增环境变量
  • fail-closed 鉴权:未设置 UPLOAD_PASSWORD 或密码少于 10 位时,所有接口全部返回 500 拒绝服务——不存在"忘配密码就裸奔"的情况;确需公开模式必须显式设置 ALLOW_ANONYMOUS_UPLOAD=true
  • 前端不保存明文密码:登录成功换取 7 天 HMAC 登录令牌(硬到期不续期,过期自动回锁屏),本机只存令牌;修改密码立即作废所有已签发令牌,可用于紧急踢人
  • 密码校验为 SHA-256 哈希比对 + 失败统一延迟 0.4s,拖慢在线暴力破解;仍请务必使用强密码(≥10 位,建议更长、真正随机)
  • 上传签名 Policy 同时绑定 Bucket 和精确对象键(eq 条件),x-oss-forbid-overwrite 也写入 Policy 条件(客户端无法剥离该字段来覆盖已有对象)
  • 分片上传有会话令牌绑定:init 时签发、绑定 key/uploadId/声明大小/分片数上限/分片尺寸;part 分片号超限直接拒绝;complete 前向 OSS ListParts 逐片核验(分片号 + ETag + 每片字节数必须与声明完全吻合),任何不符自动 Abort 清理会话——封死"声明小传大"和"检查后偷换分片"两条绕过路径
  • 请求硬化:所有接口限制请求体大小(普通接口 8KB / 分片合并 256KB)与密码、令牌、文件名等字段长度,防畸形请求消耗函数额度
  • 文件路径为 upweb/<类型>/日期/32位随机串.扩展名,不可枚举;key 额外限定安全字符集(字母数字 ._-/),杜绝注入型恶意文件名;桶本身私有 + 仅 CF IP 段只读白名单

平台层限流(强烈建议配置)

代码内的 0.4s 失败延迟只能拖慢串行猜测,无法阻止并行爆破——边缘函数是无状态的,真正的限流必须在平台 WAF 层做。三个平台免费版都自带,对 /api/* 路径按 IP 限频即可(如每分钟 20 次、突发 10 次):

  • 腾讯 EdgeOne:EO 控制台 → 站点 → 安全防护 → Web 防护 → 速率限制(CC 防护)→ 新建规则:匹配路径 /api/*,按客户端 IP 计数,超限拦截
  • Cloudflare:Security → WAF → Rate limiting rules(免费版含 1 条规则)→ 路径匹配 /api/*,period 1 minute,阈值 20,action Block
  • 阿里云 ESA:控制台 → 安全防护 → WAF → 自定义规则/频次控制 → 路径 /api/*,按 IP 限速

同时建议把密码设为真正随机的长密码(16 位以上),"至少 10 位"只是底线。 image image

About

云窝 · 阿里云 OSS + Cloudflare 免流分发的私有图床/小型网盘:密码门禁、浏览器直传 OSS、大文件分片上传与断点续传、云端文件管理,一套代码可部署到 EdgeOne Pages / 阿里云 ESA / Cloudflare Workers

Topics

Resources

Stars

9 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages