FileCodeBox 文件分享与提取码机制全指南:文本/文件分享、过期策略与分片上传 API 实战

原创2026-09-15 16:58:381,397 阅读
文章标签:后端存储MCP 服务

FileCodeBox 文件分享与提取码机制全指南:文本/文件分享、过期策略与分片上传 API 实战

导读

FileCodeBox(文件快递柜)是一个匿名口令分享系统,核心体验是"像拿快递一样取文件"——分享者上传文件或文本后获得提取码,取件者凭提取码即可获取内容。本文以官方分享指南为主体,结合仓库源码(apps/base/views.py、apps/base/services.py、apps/base/utils.py 等)深入讲解两种分享方式、五种过期策略、提取流程、分片上传与断点续传,以及错误处理与频率限制。读完本文,你将掌握 /share/* 与 /chunk/* 全部 API 的调用方式、关键配置项的作用与默认值,并能独立完成一次完整的匿名分享与提取。

分享方式概览

FileCodeBox 在首页提供两种分享入口(见下方发送页面截图):

  1. 文本分享:直接分享文本内容,适合代码片段、配置文件、公告文案等;
  2. 文件分享:上传文件进行分享,支持各种文件格式(可通过 allowed_file_types 配置白名单)。

FileCodeBox 文件发送页面:文本分享与文件分享两个标签入口

文本分享

使用方法

  1. 在首页选择「文本分享」标签;
  2. 在文本框中输入或粘贴要分享的内容;
  3. 选择过期方式和时间;
  4. 点击「分享」按钮;
  5. 获取提取码。

文本大小限制

文本分享的硬性上限为 222KB(227,328 字节)。该限制在源码中直接写死:apps/base/views.py 的 share_text 中 max_txt_size = 222 * 1024,超出时抛出 403 并提示"内容过多,建议采用文件形式"。

文本大小按 UTF-8 编码计算(len(text.encode("utf-8"))),中文字符通常占用 3 个字节。若内容超过 222KB,请改用文件分享方式。

API 接口

POST /share/text/(路由定义见 apps/base/views.py)

参数 类型 必填 说明
text string 是 要分享的文本内容
expire_value int 否 过期数值,默认 1
expire_style string 否 过期方式,默认 day

响应示例:

{
  "code": 200,
  "detail": {
    "code": "123456"
  }
}

服务端处理链路:share_text 先做 UTF-8 大小校验与 validate_expire_style 白名单校验,再调用 FileUploadService.create_text_share(apps/base/services.py)。文本分享同样走"配额预留 → 建分享记录 → 释放配额"流程:先以 text:{uuid} 为 token 预留等量存储(TTL 300 秒),写入 FileCodes 记录(prefix 固定为 "Text"、text 字段存储原文)后再释放配额,确保瞬时并发不会突破存储配额(存储配额逻辑见 apps/base/quota.py)。

文件分享

使用方法

  1. 在首页选择「文件分享」标签;
  2. 点击上传区域,或将文件拖拽到上传区域;
  3. 选择过期方式和时间;
  4. 点击「上传」按钮;
  5. 获取提取码。

支持的三种上传方式

  • 点击上传:点击上传区域选择文件;
  • 拖拽上传:将文件拖拽到上传区域;
  • 粘贴上传:从剪贴板粘贴图片(部分主题支持)。

文件大小限制与相关配置

默认单文件最大上传大小为 10MB,对应 settings.py 中 upload_size: 1024 * 1024 * 10(见 core/settings.py)。管理员可通过后台的 upload_size 配置项修改此限制。服务端在校验时(apps/base/services.py 的 validate_file_size)会对超过上限的请求返回 403。

提示:如需上传大文件,请管理员启用分片上传功能(enable_chunk=1),或适当调大 upload_size。

文件分享还会经过 apps/base/file_validation.py 的多重校验:validate_upload_file 会检查扩展名/Content-Type 与 allowed_file_types 白名单(默认 ["*"] 即不限),validate_file_magic 还会读取文件头部魔数(默认 64 字节)做内容级校验,防止伪装扩展名。

API 接口

POST /share/file/(multipart/form-data,路由定义见 apps/base/views.py)

参数 类型 必填 说明
file file 是 要上传的文件
expire_value int 否 过期数值,默认 1
expire_style string 否 过期方式,默认 day

响应示例:

{
  "code": 200,
  "detail": {
    "code": "654321",
    "name": "example.pdf"
  }
}

服务端处理链路(FileUploadService.create_file_share,apps/base/services.py):

  1. 生成存储路径:share/data/YYYY/MM/DD/{uuid}/,统一由 apps/base/utils.py 的 build_file_path 生成,普通上传、分片上传、预签名上传共用同一套路径规则;
  2. 以 file:{uuid} 为 token 预留配额(TTL 3600 秒);
  3. 写入存储后端(本地磁盘 / S3 / WebDAV / OneDrive,由 file_storage 配置决定,见 core/storage.py);
  4. 创建 FileCodes 记录;
  5. 任一步失败则调用 rollback_saved_file 回滚已落盘文件,并在 finally 中释放配额。

过期设置

FileCodeBox 支持五种灵活的过期方式:

过期方式 参数值 说明
按天过期 day 文件在指定天数后过期
按小时过期 hour 文件在指定小时后过期
按分钟过期 minute 文件在指定分钟后过期
永不过期 forever 文件永久有效
按次数过期 count 文件在被下载指定次数后过期

过期方式组合示例:

# 文件 3 天后过期
expire_value=3, expire_style=day

# 文件 12 小时后过期
expire_value=12, expire_style=hour

# 文件 30 分钟后过期
expire_value=30, expire_style=minute

# 文件永不过期
expire_value=1, expire_style=forever

# 文件被下载 5 次后过期
expire_value=5, expire_style=count

过期机制的源码实现

过期信息统一由 apps/base/utils.py 的 get_expire_info 计算,核心逻辑:

  • day / hour / minute:基于当前时间(UTC+8)加 datetime.timedelta 计算 expired_at,记录中 expired_count 保持 -1(表示按时间判定);
  • count:expired_at 设为一天后,但 expired_count 设为 expire_value,即"按下载次数过期";
  • forever:expired_at 与 expired_count 均为空/负值,永不自动过期。

数据模型 FileCodes(apps/base/models.py)中的 is_expired() 方法给出精确判定逻辑:expired_at 为 None 则永不过期;若 expired_count < 0 则按 expired_at 时间比较;否则按 expired_count <= 0(次数耗尽)判定。consume_file_usage(apps/base/views.py)在每次领取时用 SQL 原子更新 expired_count 并累加 used_count,并发场景下也不会超发。

管理员相关配置

  • expire_style:控制用户可选用的过期方式白名单(默认 ["day", "hour", "minute", "forever", "count"]),未在白名单内的请求会被 validate_expire_style 直接拒绝(400);
  • max_save_seconds:限制文件最长保存时间,get_expire_info 中若 expired_at - now 超过该上限会返回 403;为 0(默认)时按 7 天兜底限制。

过期文件的清理

过期数据由后台任务 delete_expire_files(apps/base/tasks.py)每 10 分钟巡检清理:删除 expired_at 已到或 expired_count = 0 的记录及对应存储文件,同时清理本地空目录。

提取文件

提取是分享的逆操作:输入提取码 → 查询文件信息 → 下载或查看内容。

FileCodeBox 提取文件页面:输入提取码获取文件信息与下载

使用方法

  1. 在首页的「提取文件」区域输入提取码;
  2. 点击「提取」按钮;
  3. 系统显示文件信息(文件名、大小等);
  4. 点击「下载」按钮下载文件,或直接查看文本内容。

提取码说明

  • 提取码通常为 6 位数字;
  • 永不过期的文件使用字母数字混合的提取码;
  • 提取码区分大小写(针对字母数字混合的情况)。

提取码生成策略由 code_generate_type 配置控制(默认 secret,见 core/settings.py):get_code_generate_type(apps/base/utils.py)将 secret/string 归一为字母数字混合风格,其他值则归为纯数字风格。get_random_code 在生成后会查询 FileCodes 表确保唯一性(apps/base/utils.py)。

查询文件信息

POST /share/select/

请求参数:

{
  "code": "123456"
}

响应示例(文件):

{
  "code": 200,
  "detail": {
    "code": "123456",
    "name": "example.pdf",
    "size": 1048576,
    "text": "https://example.com/download/..."
  }
}

响应示例(文本):

{
  "code": 200,
  "detail": {
    "code": "123456",
    "name": "Text",
    "size": 1024,
    "text": "This is the shared text content..."
  }
}

从源码看(apps/base/views.py 的 select_file 与 build_select_detail),text 字段对文本分享返回原文;对文件分享返回下载地址。其中有下载次数限制的文件必须走代理下载接口(download_url 指向 /share/download?...),因为第三方直链无法阻止重复使用;无次数限制的文件则直接返回存储后端的直链(如 S3 预签名地址)。同时,/share/select/ 在返回信息时已原子消费一次下载次数。

直接下载文件

GET /share/select/?code=123456

此接口(apps/base/views.py 的 get_code_file)直接返回文件内容(Content-Disposition 附件下载头,支持中文文件名),适合在浏览器中直接访问;文本分享则直接返回纯文本内容。另有带签名鉴权的代理下载接口 GET /share/download?key=...&code=...(apps/base/views.py),key 为基于提取码生成的时间窗签名 token,避免下载链接被无限转发。

元数据查询

GET/POST /share/metadata/(apps/base/views.py)只返回文件元信息(文件名、大小、类型、创建/过期时间、剩余下载次数),不消费下载次数,适合分享页卡片展示。

分片上传(大文件)

对于超过单文件上限的大文件,FileCodeBox 提供分片上传功能,需管理员启用(enable_chunk=1)。分片会话默认保留 24 小时(chunk_expire_hours,见 core/settings.py),超时未完成会被后台任务 clean_incomplete_uploads(apps/base/tasks.py)每小时清理。

分片上传流程

sequenceDiagram
    participant C as Client
    participant S as Server

    C->>S: 1. Initialize upload (POST /chunk/upload/init/)
    S-->>C: Return upload_id and chunk info

    loop Each chunk
        C->>S: 2. Upload chunk (POST /chunk/upload/chunk/{upload_id}/{chunk_index})
        S-->>C: Return chunk hash
    end

    C->>S: 3. Complete upload (POST /chunk/upload/complete/{upload_id})
    S-->>C: Return extraction code

1. 初始化上传

POST /chunk/upload/init/(apps/base/views.py)

请求参数:

{
  "file_name": "large_file.zip",
  "file_size": 104857600,
  "chunk_size": 5242880,
  "file_hash": "sha256_hash_of_file"
}
参数 类型 必填 说明
file_name string 是 文件名
file_size int 是 文件总大小(字节)
chunk_size int 否 分片大小,默认 5MB
file_hash string 是 文件的 SHA256 哈希值

响应示例:

{
  "code": 200,
  "detail": {
    "existed": false,
    "upload_id": "abc123def456",
    "chunk_size": 5242880,
    "total_chunks": 20,
    "uploaded_chunks": []
  }
}

服务端在初始化阶段会做几件关键事:

  1. 超限预检:按 total_chunks × chunk_size 估算理论上传量,超过 upload_size 直接 403(apps/base/views.py);
  2. 配额预留:以 chunk:{upload_id} 为 token 预留 file_size 的存储配额(TTL 为 chunk_expire_hours),避免未完成的大文件拖垮存储(apps/base/views.py);
  3. 会话标记:以 chunk_index = -1 的特殊记录作为上传会话头(apps/base/models.py 的 UploadChunk 模型);
  4. 文件名会经 sanitize_filename 清洗防路径穿越,并校验文件类型。

2. 上传分片

POST /chunk/upload/chunk/{upload_id}/{chunk_index}(multipart/form-data,apps/base/views.py)

  • upload_id:初始化时返回的上传会话 ID;
  • chunk_index:分片索引,从 0 开始。

请求体为分片文件数据。响应示例:

{
  "code": 200,
  "detail": {
    "chunk_hash": "sha256_hash_of_chunk"
  }
}

服务端对每个分片做完整性校验:分片索引合法性、分片大小不超过声明值、累计上传量不超过 upload_size;首分片(chunk_index == 0)还会校验文件头魔数以确认真实类型。每个分片计算 SHA256 存入记录,保存成功后才标记 completed=True,保证记录与存储一致。

3. 完成上传

POST /chunk/upload/complete/{upload_id}(apps/base/views.py)

请求参数:

{
  "expire_value": 1,
  "expire_style": "day"
}

响应示例:

{
  "code": 200,
  "detail": {
    "code": "789012",
    "name": "large_file.zip"
  }
}

完成阶段调用 FileUploadService.complete_chunked_upload(apps/base/services.py):先校验分片完整性(已上传分片数必须等于 total_chunks),再由存储后端 merge_chunks 按索引顺序合并分片并计算整文件哈希,然后创建 FileCodes 分享记录(标记 is_chunked=True、记录 file_hash 与 upload_id),最后清理分片文件与数据库记录、释放配额。

断点续传

分片上传天然支持断点续传。若上传中断,恢复流程为:

  1. 重新调用初始化接口,使用相同的 file_hash;
  2. 服务器按 chunk_hash + file_size + file_name 匹配未完成的会话,返回已上传分片索引列表 uploaded_chunks;
  3. 客户端只需上传未在列表中的分片;对已上传的分片重复提交时,服务端会直接返回 {"chunk_hash": ..., "skipped": true} 跳过。

辅助接口

  • GET /chunk/upload/status/{upload_id}:查询上传状态,返回已上传分片列表与 progress 百分比(apps/base/views.py);
  • DELETE /chunk/upload/{upload_id}:取消上传,清理存储中的临时分片与数据库记录并释放配额(apps/base/views.py)。

错误处理

常见错误码

错误码 说明 解决方案
403 文件大小超过限制 减小文件大小,或联系管理员调整 upload_size / 启用分片上传
403 内容过多 文本超过 222KB,请改用文件分享
403 上传频率限制 等待限制时间窗口过后重试
404 文件不存在 检查提取码是否正确
404 文件已过期 文件已过期或下载次数已用完

频率限制

为防止滥用,系统对上传、元数据查询与提取操作实施基于 IP 的频率限制(apps/base/dependencies.py 的 IPRateLimit 实现,限额配置见 core/settings.py):

  • 上传限制:默认 upload_count=10 / upload_minute=1,即每分钟最多 10 次上传;
  • 错误限制:默认 error_count=10 / error_minute=1,即每分钟最多 10 次错误尝试(提取失败、下载鉴权失败等都会计数);
  • 元数据查询复用错误限制额度。

命中限制后请等待时间窗口(1 分钟)过后再重试。限流计数为内存态,并会由 delete_expire_files 定时清理过期 IP 记录。error_count、error_minute、upload_count、upload_minute 均可在后台配置。

分享相关配置速查

以下配置项直接决定分享体验,均在管理面板中可调,默认值见 core/settings.py:

配置项 默认值 作用
upload_size 10MB 单文件/单次分片上传大小上限
expire_style 全部五种 用户可选的过期方式白名单
max_save_seconds 0(按 7 天兜底) 文件最长保存时间上限
enable_chunk 0 是否启用分片上传
chunk_expire_hours 24 分片上传会话有效时长
code_generate_type secret 提取码风格(数字 / 字母数字混合)
allowed_file_types ["*"] 允许上传的文件类型白名单
upload_count / upload_minute 10 / 1 上传频率限制
error_count / error_minute 10 / 1 错误尝试频率限制
open_upload 1 是否开放匿名上传

配置修改后由 apps/base/config.py 的 refresh_settings 从数据库热加载到运行时(带 2 秒 TTL 缓存),无需重启服务。

下一步

登录后查看全文
FileCodeBox