Help us shape the future of Go! Take the 2026 Go Developer Survey

chunk

package
v0.18.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 12, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Overview

storage.go — 分片物理操作的存储接缝(chunk 域)。

写片/合并/取流/清理收口于 app 层,HTTP 适配层不直连 storage; 磁盘布局(uploads/YYYY/MM/DD/<uuid><ext>)统一走 utils.UploadObjectRelPath, 与直传分享落盘同源。未注入统一存储实例时(测试/降级路径)懒加载本地后端 兜底,参数与历史 handler 兜底一致(./data)。

token.go 分片会话令牌与归属校验的单一实现。

此前 transport/handler(multi-bind)与 gen/handler/chunk 各持一份逐字孪生 (chunkSessionToken/verifyChunkToken/ownedByCaller,注释自认"同源同语义"), 收口到本域导出函数后两处只留薄委托。

Index

Constants

View Source
const MaxTotalChunks = 10000

MaxTotalChunks 单会话分片数硬上限(2026-10-08 加固)。TotalChunks 客户端 可控且此前无上限:一次 init 可声明上亿分片刷控制行,后续每个 chunk PUT 还会按 index 逐条建行。10000 片 × 常见 4MB 分片已承载 40GB,远超常规 upload.max_file_size 配置,正常客户端不可能触顶。

Variables

View Source
var ErrSessionConflict = errors.New("上传会话已被占用,请刷新上传")

ErrSessionConflict init 撞上他人进行中(已含分片数据)的会话。 2026-10-05 审计 P1:uploadID 可为客户端自报哈希(公开文件哈希可预测), 原"最后写入者 wins"无条件重绑归属让第三方可截胡他人上传——改归属后即可 绑走已传分片(内容窃取)或覆写合并结果(内容替换)。HTTP 层映射 409。

Functions

func OwnedByCaller

func OwnedByCaller(info *model.UploadChunk, clientIP string, sessionToken string, callerUserID *uint) bool

OwnedByCaller 分片会话归属校验(治理):控制记录记有 OwnerIP 时,要求 IP 一致、同一登录用户或持有效会话令牌;老数据(OwnerIP 为空)此前无条件 放行——同网任意调用方可向他人进行中会话写分片/取消/完成(2026-10-08 加固 收紧为 fail-closed:仅令牌或同用户可通过,匿名老会话不可恢复,分片会话是 短生命周期数据,升级窗口外无存量)。 clientIP/callerUserID 由调用方按各路由的身份来源提取后传入(JWT/匿名、 ctx 值/c.Get 两种路径判定口径一致)。

func SessionToken

func SessionToken(uploadID string) string

SessionToken 分片会话令牌:HMAC("chunk-session:"+uploadID, key)。客户端从 init 响应头 X-Upload-Token 取得并在后续请求回传,即可在 IP 漂移(移动网络/ CGNAT)后仍通过归属校验。密钥复用 presign 签名密钥(conf.PresignSigningKey 单源:PB_PRESIGN_SIGNING_KEY 优先,缺省回退 jwt_secret);无密钥返回空串 (令牌通道关闭,仅 IP/用户匹配生效)。

func ValidateChunkPlan added in v0.14.9

func ValidateChunkPlan(fileSize, chunkSize int64, totalChunks int) error

ValidateChunkPlan 校验分片计划自洽性:TotalChunks 必须恰为 ⌈FileSize/ChunkSize⌉ 且不超过 MaxTotalChunks。防两类滥用:① 总数虚高(配合逐片 PUT 刷 DB 行/ 存储写);② 总数不足让 Complete 永不满足的僵尸会话占库。自家前端按 Math.ceil(file.size/chunkSize) 计算,严格相等不误伤。

func VerifySessionToken

func VerifySessionToken(uploadID, token string) bool

VerifySessionToken 客户端回传的会话令牌是否有效。

Types

type ChunkResp

type ChunkResp struct {
	ID          uint   `json:"id"`
	UploadID    string `json:"upload_id"`
	ChunkIndex  int    `json:"chunk_index"`
	ChunkHash   string `json:"chunk_hash"`
	TotalChunks int    `json:"total_chunks"`
	FileSize    int64  `json:"file_size"`
	ChunkSize   int    `json:"chunk_size"`
	FileName    string `json:"file_name"`
	Completed   bool   `json:"completed"`
	Status      string `json:"status"`
}

type InitiateUploadReq

type InitiateUploadReq struct {
	UploadID     string
	FileName     string
	TotalChunks  int
	FileSize     int64
	ChunkSize    int
	OwnerIP      string // 上传者 IP(治理归属追踪)
	UserID       *uint  // 上传者(chunk 路由当前无可选认证,恒 nil;字段就位以便接线)
	SessionToken string // 客户端回传的会话令牌(X-Upload-Token;IP 漂移后恢复会话用)
}

type ProgressResp

type ProgressResp struct {
	UploadID        string  `json:"upload_id"`
	TotalChunks     int     `json:"total_chunks"`
	CompletedChunks int64   `json:"completed_chunks"`
	Progress        float64 `json:"progress"`
	Status          string  `json:"status"`
}

type Service

type Service struct {
	// contains filtered or unexported fields
}

func NewService

func NewService(gormDB *gorm.DB) *Service

func (*Service) CheckUploadProgress

func (s *Service) CheckUploadProgress(ctx context.Context, uploadID string) (*ProgressResp, error)

CheckUploadProgress 检查上传进度

func (*Service) CleanSessionFiles

func (s *Service) CleanSessionFiles(ctx context.Context, uploadID string) error

CleanSessionFiles 清理上传会话的全部临时分片。

func (*Service) CompleteUpload

func (s *Service) CompleteUpload(ctx context.Context, uploadID string) error

CompleteUpload 完成上传

func (*Service) DeleteMergedFile

func (s *Service) DeleteMergedFile(ctx context.Context, relPath string)

DeleteMergedFile 尽力删除合并对象(校验失败清理路径;删除失败不阻断主流程, 与历史 `_ = DeleteFile` 语义一致)。

func (*Service) DeleteUpload

func (s *Service) DeleteUpload(ctx context.Context, uploadID string) error

DeleteUpload 删除上传

func (*Service) FindQuickUploadSource added in v0.17.0

func (s *Service) FindQuickUploadSource(ctx context.Context, userID uint, fileHash string, fileSize int64) (*model.FileCode, error)

FindQuickUploadSource 秒传源检索(2026-10-12,D1=A 同用户内去重): 返回调用者自己历史同 hash+size 的最新可用分享;命中后由 handler 按**本次 元数据**建新分享引用其存储对象(与 presign 通道同语义,2026-10-12 起 chunk init 契约携带 expire/password 元数据)。

此前"命中复用自己旧码"语义已废弃:旧分享的过期/密码/名称与本次请求意图 不符(静默丢弃本次元数据);全库跨用户匹配更早废弃——存在性 oracle。

func (*Service) GetUploadInfo

func (s *Service) GetUploadInfo(ctx context.Context, uploadID string) (*model.UploadChunk, error)

GetUploadInfo 获取上传信息(包括文件名、大小等)

func (*Service) GetUploadList

func (s *Service) GetUploadList(ctx context.Context, page, pageSize int) ([]*ChunkResp, int64, error)

GetUploadList 获取上传列表

func (*Service) GetUploadedChunkIndexes

func (s *Service) GetUploadedChunkIndexes(ctx context.Context, uploadID string) ([]int, error)

GetUploadedChunkIndexes 获取已上传分片的索引列表

func (*Service) InitiateUpload

func (s *Service) InitiateUpload(ctx context.Context, req *InitiateUploadReq) (*ChunkResp, error)

InitiateUpload 初始化分片上传

func (*Service) MergeUpload

func (s *Service) MergeUpload(ctx context.Context, uploadID string, totalChunks int, fileName string) (string, error)

MergeUpload 合并全部分片为正式上传对象,返回统一布局下的相对路径。 文件名仅用于扩展名推导,磁盘名由 UploadObjectRelPath 生成(UUID)。

func (*Service) OpenMerged

func (s *Service) OpenMerged(ctx context.Context, relPath string) (io.ReadCloser, int64, error)

OpenMerged 打开合并后的对象读取流(哈希计算/魔数复检用),返回读取器与实际大小。

func (*Service) SaveChunkData

func (s *Service) SaveChunkData(ctx context.Context, uploadID string, chunkIndex int, data []byte) error

SaveChunkData 写入单个分片。

func (*Service) SetStorage

func (s *Service) SetStorage(st storage.StorageInterface)

SetStorage 注入统一存储实例(bootstrap 调用)。回归要点:chunk 与 share 必须共用同一实例——此前两个懒加载单例路径基不一致(./data 与 ./data/uploads), 分片合并写入 data/uploads/<rel>、下载却找 data/uploads/uploads/<rel>。

func (*Service) UploadChunk

func (s *Service) UploadChunk(ctx context.Context, req *UploadChunkReq) (*ChunkResp, error)

UploadChunk 上传单个分片

type UploadChunkReq

type UploadChunkReq struct {
	UploadID   string
	ChunkIndex int
	ChunkHash  string
	ChunkSize  int
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL