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

presign

package
v0.21.2 Latest Latest
Warning

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

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

Documentation

Overview

Package presign 实现预签名上传 service。

流程:

  1. 客户端调 Init 申请预签名 URL + token
  2. 客户端 PUT 直传文件到 URL
  3. 客户端调 Complete 通知服务写 share 表
  4. 服务返回 share_code

当前实现:fallback 走"自家直传 URL + token"模式(不依赖真实 S3/OSS 预签名)。 当 OpenDAL binding 接入后,可直接生成真实预签名 URL。

Index

Constants

View Source
const (
	// SchemeSelf 自家中转:客户端 PUT /api/v1/presign/upload-direct/:uploadID
	SchemeSelf = "self"
	// SchemeS3 真·预签名直传:客户端 PUT 对象存储,服务器只签名不过流量
	SchemeS3 = "s3"
)

直传模式标记(meta.Scheme / InitResult.Scheme,服务端判定为准)

Variables

View Source
var (
	ErrUploadNotFound  = errors.New("upload not found")
	ErrTokenInvalid    = errors.New("upload token invalid")
	ErrUploadExpired   = errors.New("upload expired")
	ErrAlreadyComplete = errors.New("upload already completed")
)

错误定义

Functions

This section is empty.

Types

type CompleteResult

type CompleteResult struct {
	*InitMeta
	ShareCode    string
	ShareURL     string
	FullShareURL string
	OwnerIP      string
	PickupCode   string // 6 位取件码(share 域铸造;永久分享/未注入 minter 为空)
}

CompleteResult Complete 返回结果(包含 share_code)

type InitMeta

type InitMeta struct {
	UploadID    string    `json:"upload_id"`
	ObjectKey   string    `json:"object_key"`
	FileName    string    `json:"file_name"`
	FileSize    int64     `json:"file_size"`
	ContentType string    `json:"content_type"`
	Scheme      string    `json:"scheme"`
	ExpireAt    time.Time `json:"expire_at"`
	Complete    bool      `json:"complete"`
	UserID      uint      `json:"user_id"`
	ExpireValue int32     `json:"expire_value"`
	ExpireStyle string    `json:"expire_style"`
	RequireAuth bool      `json:"require_auth"`
	// PasswordHash 为 require_auth=true 时分享密码的 bcrypt 哈希(明文不落存储)
	PasswordHash string `json:"password_hash,omitempty"`
	// FileHash 直传完成后服务端计算的 SHA-256(写入 file_codes.file_hash,秒传依据)
	// SessionTTL 直传会话/签名时效(handler 按管理配置下发;0=服务端默认 1h)
	SessionTTL time.Duration `json:"-"`
	FileHash   string        `json:"file_hash,omitempty"`
	// PickupCode 匿名取件码直传绑定(2026-10-07):非空时 Complete 不新建分享,
	// 而是把对象回填到该占位记录(/anonymous/generate 创建)。有效期/密码以
	// 占位记录为准——本 meta 的 expire/auth 字段在绑定路径被忽略。
	PickupCode string `json:"pickup_code,omitempty"`
	// Note 文件备注(可空,随分享展示给取件人;2026-10-12 #489)
	Note string `json:"note,omitempty"`
}

InitMeta init 元信息

type InitResult

type InitResult struct {
	UploadID      string            `json:"upload_id"`
	UploadURL     string            `json:"upload_url"`
	Method        string            `json:"method"`
	Headers       map[string]string `json:"headers"`
	ExpireSeconds int32             `json:"expire_seconds"`
	ObjectKey     string            `json:"object_key"`
	Scheme        string            `json:"scheme"`
	Token         string            `json:"token"`
}

InitResult init 返回

type ObjectStore

type ObjectStore interface {
	PresignPutURL(ctx context.Context, objectKey string, expire time.Duration) (string, error)
	// HeadObject 直传完成核实用:对象必须真实存在并取实际大小(S3 为事实源)
	HeadObject(ctx context.Context, objectKey string) (size int64, etag string, err error)
}

ObjectStore 真预签名直传能力(由 *storage.StorageService 实现)。 PresignPutURL 对 local/webdav 返回 storage.ErrPresignUnsupported,调用方回退自家中转。

type PickupBinder added in v0.14.5

type PickupBinder interface {
	// ResolvePlaceholder 校验取件码是待回填占位分享,返回其 8 位分享码
	ResolvePlaceholder(ctx context.Context, pickupCode string) (string, error)
	// BindFilePath 把直传对象回填到占位分享(file_path+size;note 非空一并写入)
	BindFilePath(ctx context.Context, shareCode, filePath string, size int64, note string) error
}

PickupBinder 匿名取件码占位绑定窄接口(app/anonymous.Service 结构性满足; presign 域不 import anonymous 域,由 bootstrap 注入)。

type QuickShareMeta added in v0.17.0

type QuickShareMeta struct {
	FileName     string
	FileSize     int64
	FileHash     string
	ExpireValue  int
	ExpireStyle  string
	RequireAuth  bool
	PasswordHash string
	Note         string // 文件备注(可空,取件页展示;2026-10-12 #489)
}

QuickShareMeta 秒传建分享所需元数据(presign init 请求携带)。

type QuickUploadResult

type QuickUploadResult struct {
	ShareCode    string
	FullShareURL string
}

QuickUploadResult 秒传命中结果

type Service

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

Service 预签名上传 service

func NewService

func NewService(gormDB *gorm.DB, rdb *redis.Client, baseURL string, signingKey string) *Service

NewService 创建 service。rdb nil 归一化:typed-nil 接口会骗过 "Redis 可用"守卫(!= nil 判真)。 rdb 为 nil(redis.host 未配置)时进入单机内存模式:直传会话存进程内 TTL KV,预签名直传全功能可用(单进程语义等价;重启未完成会话作废)。

func (*Service) Abort

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

Abort 取消

func (*Service) CheckQuickUpload

func (s *Service) CheckQuickUpload(ctx context.Context, userID uint, meta QuickShareMeta) (*QuickUploadResult, error)

CheckQuickUpload 秒传检查(2026-10-12 重做,D1=A 同用户内去重): 命中**调用者自己**历史同 hash+size 文件时,新建一条分享(调用者元数据: 名称/过期/密码)引用既有存储对象,免直传直接出码。

此前语义=回借既有分享的 code(全库匹配)——两处不可接受:跨用户匹配是 存在性 oracle(撞库探测谁传过什么);借来的分享生命周期/密码/过期全归 原主,本次请求的元数据被静默丢弃。现改为同用户 + 建新分享。

func (*Service) Complete

func (s *Service) Complete(ctx context.Context, uploadID, token, ownerIP string) (*CompleteResult, error)

Complete 完成通知(调 share service 写分享表)

func (*Service) GetMeta

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

GetMeta 查询(管理后台用)

func (*Service) Init

func (s *Service) Init(ctx context.Context, meta InitMeta) (*InitResult, error)

Init 申请预签名

func (*Service) SetObjectStore

func (s *Service) SetObjectStore(o ObjectStore)

SetObjectStore 注入真预签名直传能力(s3 后端;不支持时 Init 自动回退自家中转)

func (*Service) SetPickupBinder added in v0.14.5

func (s *Service) SetPickupBinder(b PickupBinder)

SetPickupBinder 注入匿名取件码占位绑定(anonymous 域实现;nil = 绑定通道不可用)

func (*Service) SetShareService

func (s *Service) SetShareService(svc ShareServiceInterface)

SetShareService 注入 share service(用于 Complete 时写分享表)

func (*Service) SetStorage

func (s *Service) SetStorage(st StorageWriter)

SetStorage 注入存储服务(直传按当前激活后端落盘,含路径防御)

func (*Service) UploadDirect

func (s *Service) UploadDirect(ctx context.Context, uploadID, token string, body io.Reader, declaredSize int64) error

UploadDirect 处理预签名直传:校验 token → 流式写文件(meta.ObjectKey 路径)。 由 bootstrap 注册的 PUT /api/v1/presign/upload-direct/:uploadID 端点调用。 body 流式消费(配合 hertz StreamRequestBody),大文件不再整体进内存; 写入字节数必须与 init 申报的 meta.FileSize 完全一致,超出即拒并清理。

type ShareCreateReq added in v0.15.2

type ShareCreateReq struct {
	Channel      string // 上传通道(metrics 用;presign 恒 "presign")
	FilePath     string
	Size         int64
	Text         string
	ExpiredAt    *time.Time
	ExpiredCount int
	RequireAuth  bool
	PasswordHash string
	UserID       *uint
	UploadType   string
	OwnerIP      string
	FileHash     string
	UploadID     string
	Note         string // 文件备注(可空,取件页展示;2026-10-12 #489)
}

ShareCreateReq 经 share 域建分享的窄化请求(消费侧词汇表)。 此前本域直接 import share.ShareFileReq/ShareResp 构成唯一约定 app 跨域边 (presign→share),2026-10-10 消解:字段与 share.CreateShare 对齐, bootstrap 适配器(presignShareAdapter)转换到 share 域类型——与 requestShareGateway/mcpShareAdapter 同款装配点转换模式。

type ShareCreateResp added in v0.15.2

type ShareCreateResp struct {
	Code         string
	ShareURL     string
	FullShareURL string
	PickupCode   string
}

ShareCreateResp share 域建分享结果的窄视图(presign 消费字段)。

type ShareServiceInterface

type ShareServiceInterface interface {
	CreateShare(ctx context.Context, req *ShareCreateReq) (*ShareCreateResp, error)
}

ShareServiceInterface share service 接口(消费侧窄接口,bootstrap 注入 share 域适配器;避免 app 跨域 import)。

type StorageWriter

type StorageWriter interface {
	SaveBytes(ctx context.Context, savePath string, data []byte) error
	DeleteFile(ctx context.Context, filePath string) error
}

StorageWriter 存储写入抽象(由 *storage.StorageService 实现)。 单方法接口避免反向依赖存储包,也保持既有 StorageInterface/mock 不动。

Jump to

Keyboard shortcuts

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