Documentation
¶
Index ¶
- Constants
- func BuildContentDisposition(filename string) string
- func IsAllowedExtension(ext string) bool
- func IsBlockedExtension(ext string) bool
- func ValidateMagicNumber(ext string, header []byte) bool
- type File
- type IService
- type IUploadService
- type PresignedGetter
- type PresignedPutter
- type SeaweedFS
- func (s *SeaweedFS) DownloadURL(path string, filename string) (string, error)
- func (s *SeaweedFS) GetFile(path string) (io.ReadCloser, string, error)
- func (s *SeaweedFS) PresignedGetURL(objectPath string, filename string, disposition string, expires time.Duration) (string, error)
- func (s *SeaweedFS) PresignedPutURL(objectPath string, contentType string, contentDisposition string, ...) (string, string, error)
- func (s *SeaweedFS) UploadFile(filePath string, contentType string, contentDisposition string, ...) (map[string]interface{}, error)
- type Service
- func (s *Service) DownloadAndMakeCompose(uploadPath string, downloadURLs []string) (map[string]interface{}, error)
- func (s *Service) DownloadImage(url string, ctx context.Context) (io.ReadCloser, error)
- func (s *Service) DownloadURL(path string, filename string) (string, error)
- func (s *Service) GetFile(path string) (io.ReadCloser, string, error)
- func (s *Service) MakeCompose(srcImgFiles []io.ReadCloser) (image.Image, error)
- func (s *Service) PresignedGetURL(objectPath string, filename string, disposition string, expires time.Duration) (string, error)
- func (s *Service) PresignedPutURL(objectPath string, contentType string, contentDisposition string, ...) (string, string, error)
- func (s *Service) UploadFile(filePath string, contentType string, contentDisposition string, ...) (map[string]interface{}, error)
- type ServiceCOS
- func (sc *ServiceCOS) DownloadURL(ph string, filename string) (string, error)
- func (sc *ServiceCOS) GetFile(ph string) (io.ReadCloser, string, error)
- func (sc *ServiceCOS) PresignedGetURL(objectPath string, filename string, disposition string, expires time.Duration) (string, error)
- func (sc *ServiceCOS) PresignedPutURL(objectPath string, contentType string, contentDisposition string, ...) (uploadURL string, downloadURL string, err error)
- func (sc *ServiceCOS) UploadFile(filePath string, contentType string, contentDisposition string, ...) (map[string]interface{}, error)
- type ServiceMinio
- func (sm *ServiceMinio) DownloadURL(ph string, filename string) (string, error)
- func (sm *ServiceMinio) GetFile(ph string) (io.ReadCloser, string, error)
- func (sm *ServiceMinio) PresignedGetURL(objectPath string, filename string, disposition string, expires time.Duration) (string, error)
- func (sm *ServiceMinio) PresignedPutURL(objectPath string, contentType string, contentDisposition string, ...) (uploadURL string, downloadURL string, err error)
- func (sm *ServiceMinio) UploadFile(filePath string, contentType string, contentDisposition string, ...) (map[string]interface{}, error)
- type ServiceOSS
- func (s *ServiceOSS) DownloadURL(path string, filename string) (string, error)
- func (s *ServiceOSS) GetFile(path string) (io.ReadCloser, string, error)
- func (s *ServiceOSS) PresignedGetURL(objectPath string, filename string, disposition string, expires time.Duration) (string, error)
- func (s *ServiceOSS) PresignedPutURL(objectPath string, contentType string, contentDisposition string, ...) (uploadURL string, downloadURL string, err error)
- func (s *ServiceOSS) UploadFile(filePath string, contentType string, contentDisposition string, ...) (map[string]interface{}, error)
- type ServiceQiniu
- func (s *ServiceQiniu) DownloadURL(path string, filename string) (string, error)
- func (s *ServiceQiniu) GetFile(path string) (io.ReadCloser, string, error)
- func (s *ServiceQiniu) PresignedGetURL(objectPath string, filename string, disposition string, expires time.Duration) (string, error)
- func (s *ServiceQiniu) PresignedPutURL(objectPath string, contentType string, contentDisposition string, ...) (string, string, error)
- func (s *ServiceQiniu) UploadFile(filePath string, contentType string, contentDisposition string, ...) (map[string]interface{}, error)
- type ServiceS3
- func (s *ServiceS3) DownloadURL(ph string, filename string) (string, error)
- func (s *ServiceS3) GetFile(ph string) (io.ReadCloser, string, error)
- func (s *ServiceS3) PresignedGetURL(objectPath string, filename string, disposition string, expires time.Duration) (string, error)
- func (s *ServiceS3) PresignedPutURL(objectPath string, contentType string, contentDisposition string, ...) (uploadURL string, downloadURL string, err error)
- func (s *ServiceS3) UploadFile(filePath string, contentType string, contentDisposition string, ...) (map[string]interface{}, error)
- type Type
Constants ¶
const MaxFileSize int64 = 100 * 1024 * 1024
MaxFileSize 最大文件大小(100MB)
const StickerMaxDimension = 512
StickerMaxDimension 自定义贴纸解码后单边像素上限(512×512)。文件大小上限并不 约束解码维度:一张高压缩比的小文件可解出极大位图(decompression bomb),把内联 渲染端的内存撑爆——而贴纸会发送给会话对方,等于跨用户 DoS。上传时用 image.DecodeConfig 只读图像头拿 W×H(不解整图)并卡死此上限。
const StickerMaxFileSize int64 = 1 * 1024 * 1024
StickerMaxFileSize 自定义贴纸单文件上限(1MB)。贴纸是高频内联渲染的小图, 收紧到 1MB(对标业界:Discord 贴纸 512KB、微信 ~1MB),避免大图占用与卡顿。
Variables ¶
This section is empty.
Functions ¶
func BuildContentDisposition ¶
func ValidateMagicNumber ¶
ValidateMagicNumber 验证文件内容的魔数是否与扩展名匹配 返回 true 表示验证通过(内容与扩展名一致或该扩展名无需验证)
Types ¶
type IService ¶
type IService interface {
IUploadService
DownloadAndMakeCompose(uploadPath string, downloadURLs []string) (map[string]interface{}, error)
DownloadImage(url string, ctx context.Context) (io.ReadCloser, error)
// PresignedPutURL signs a direct-to-storage PUT URL. `fileSize` is the
// exact byte length the client commits to upload; the storage backend
// signs `Content-Length: <fileSize>` (or its OSS equivalent) into the
// canonical headers, so any deviation at PUT time is rejected by the
// gateway with a SignatureDoesNotMatch / SizeMismatch response. This
// is what lets the presigned-PUT path enforce the same MaxFileSize cap
// the multipart `uploadFile` handler enforces server-side; without it
// any authenticated caller could upload arbitrary bytes under an
// allowed extension and bypass the size gate entirely.
PresignedPutURL(objectPath string, contentType string, contentDisposition string, fileSize int64, expires time.Duration) (uploadURL string, downloadURL string, err error)
PresignedGetURL(objectPath string, filename string, disposition string, expires time.Duration) (string, error)
}
IService IService
type IUploadService ¶
type IUploadService interface {
UploadFile(filePath string, contentType string, contentDisposition string, copyFileWriter func(io.Writer) error) (map[string]interface{}, error)
// 获取下载地址
DownloadURL(path string, filename string) (string, error)
// 直接读取文件内容(用于 MinIO 等非 public bucket)
GetFile(path string) (io.ReadCloser, string, error)
}
type PresignedGetter ¶
type PresignedPutter ¶
type SeaweedFS ¶
func NewSeaweedFS ¶
func (*SeaweedFS) DownloadURL ¶
func (*SeaweedFS) PresignedGetURL ¶
func (s *SeaweedFS) PresignedGetURL(objectPath string, filename string, disposition string, expires time.Duration) (string, error)
PresignedGetURL is intentionally not implemented for SeaweedFS.
Public SeaweedFS reads are unsigned — the volume URL itself is the download URL. Callers that want a signed download against SeaweedFS should use the regular DownloadURL path; the IService surface keeps the hook here so the type assertion in service.go succeeds and we fail loudly only when a signed URL is genuinely required.
func (*SeaweedFS) PresignedPutURL ¶
func (s *SeaweedFS) PresignedPutURL(objectPath string, contentType string, contentDisposition string, fileSize int64, expires time.Duration) (string, string, error)
PresignedPutURL is intentionally not implemented for SeaweedFS.
The standard SeaweedFS Filer/Volume HTTP API does not expose a presigned upload primitive: uploads go through the multipart-form helper used by UploadFile above, and the volume server authenticates by IP / network segmentation rather than by signed URL. Returning a clear error keeps the IService surface uniform; deployments needing browser-direct upload should sit a presign-capable proxy (or the SeaweedFS S3 gateway, when configured) in front of SeaweedFS, or fall back to server-side upload via UploadFile.
type Service ¶
Service Service
func (*Service) DownloadAndMakeCompose ¶
func (s *Service) DownloadAndMakeCompose(uploadPath string, downloadURLs []string) (map[string]interface{}, error)
DownloadAndMakeCompose 下载并组合图片
func (*Service) DownloadImage ¶
func (*Service) DownloadURL ¶
func (*Service) MakeCompose ¶
MakeCompose 组合图片
func (*Service) PresignedGetURL ¶
func (*Service) PresignedPutURL ¶
type ServiceCOS ¶
ServiceCOS 腾讯云COS文件上传(通过S3兼容协议)
func NewServiceCOS ¶
func NewServiceCOS(ctx *config.Context) *ServiceCOS
NewServiceCOS NewServiceCOS
func (*ServiceCOS) DownloadURL ¶
func (sc *ServiceCOS) DownloadURL(ph string, filename string) (string, error)
DownloadURL builds a browser-facing object URL that respects the addressing style chosen by `publicEndpoint`. The result MUST land on the same host (and path shape) as the presigned GET URL emitted by `PresignedGetURL`, otherwise an upload-then-download flow returns 404 even when the PUT succeeded.
Hotfix history: this function previously concatenated `BucketURL` with the object key directly, which silently dropped the bucket segment for path-style CDN deployments (BucketURL=`https://cdn.example.com`):
- PresignedPutURL → `https://cdn.example.com/<bucket>/<prefix>/<key>` ✅ (signed by `newPublicClient` with `BucketLookupPath`)
- DownloadURL → `https://cdn.example.com/<prefix>/<key>` ❌ (missing bucket segment → next browser GET = 404)
`PresignedPutURL` calls `DownloadURL` to populate the `downloadUrl` field returned by `/v1/file/upload-credentials`, so the mismatch shipped to every browser client. This is the YUJ-848 follow-up to the YUJ-846 path-style fix in PR#56 — the sibling `PresignedPutURL` / `PresignedGetURL` paths got `BucketLookupPath` in PR#56, and this function now matches.
func (*ServiceCOS) GetFile ¶
func (sc *ServiceCOS) GetFile(ph string) (io.ReadCloser, string, error)
DownloadURL 获取COS文件下载地址
func (*ServiceCOS) PresignedGetURL ¶
func (sc *ServiceCOS) PresignedGetURL(objectPath string, filename string, disposition string, expires time.Duration) (string, error)
PresignedGetURL 生成预签名 GET URL,带 response-content-disposition 用于下载。
Client selection follows the same logic as PresignedPutURL: bucket-subdomain / empty BucketURL → sign via `newPublicClient`; CDN alias BucketURL → sign via `getClient` (canonical COS endpoint) because the CDN domain has no route for path-style `/<bucket>/<key>`. (YUJ-877 / GH#57 fix — see PresignedPutURL for the full rationale.)
func (*ServiceCOS) PresignedPutURL ¶
func (sc *ServiceCOS) PresignedPutURL(objectPath string, contentType string, contentDisposition string, fileSize int64, expires time.Duration) (uploadURL string, downloadURL string, err error)
PresignedPutURL 生成预签名 PUT URL,用于客户端直传 COS。
Client selection depends on the BucketURL shape:
Bucket-subdomain BucketURL (e.g. `https://<bucket>.cos.example.com`) or empty BucketURL: sign against the browser-facing endpoint via `newPublicClient`. SigV4 covers `host`, so the browser hits the same host the signature covers.
CDN alias BucketURL (e.g. `https://cdn.deepminer.com.cn`): sign against the canonical COS endpoint via `getClient` (virtual-hosted `<bucket>.cos.<region>.myqcloud.com`). CDN domains are bucket aliases — the CDN origin routes to the bucket implicitly, so the SDK's path-style `/<bucket>/<key>` URL shape has no corresponding route on the CDN. Signing against the real COS endpoint means the browser uploads/downloads directly to COS, bypassing the CDN. `publicURL` uses the CDN for non-presigned download URLs only. (YUJ-877 / GH#57 fix)
fileSize is signed into the canonical-headers section as `Content-Length`. The browser MUST echo the same value (browsers compute it automatically from the request body length); any mismatch is rejected by COS as 403 SignatureDoesNotMatch — same enforcement model as the MinIO backend, see service_minio.go for the rationale.
type ServiceMinio ¶
ServiceMinio 文件上传
func NewServiceMinio ¶
func NewServiceMinio(ctx *config.Context) *ServiceMinio
NewServiceMinio NewServiceMinio
func (*ServiceMinio) DownloadURL ¶
func (sm *ServiceMinio) DownloadURL(ph string, filename string) (string, error)
func (*ServiceMinio) GetFile ¶
func (sm *ServiceMinio) GetFile(ph string) (io.ReadCloser, string, error)
func (*ServiceMinio) PresignedGetURL ¶
func (sm *ServiceMinio) PresignedGetURL(objectPath string, filename string, disposition string, expires time.Duration) (string, error)
PresignedGetURL generates a presigned GET URL with a Content-Disposition override so the browser saves the file under the correct user-facing filename. The URL is signed against the browser-facing endpoint (`publicEndpoint`); no post-sign host rewriting is performed. MinIO 默认 bucket 为公共读,但鉴权模式下也通过此方法签发。
func (*ServiceMinio) PresignedPutURL ¶
func (sm *ServiceMinio) PresignedPutURL(objectPath string, contentType string, contentDisposition string, fileSize int64, expires time.Duration) (uploadURL string, downloadURL string, err error)
PresignedPutURL generates a presigned PUT URL the browser can use to upload directly to MinIO, plus the matching anonymous GET URL for the resulting object. The target bucket is bootstrapped on first use via `ensureBucket` so a presigned PUT against a fresh deployment never lands on a NoSuchBucket response.
The returned URL is signed against the *browser-facing* endpoint (`publicEndpoint`), not the server-internal one. SigV4 includes `host` in the signed headers, so any post-sign host change would invalidate the signature; signing with the public host up front is the only way for the resulting URL to be valid as-is from a browser. Bucket bootstrap still runs against the internal client because it needs network reachability, not signature validity for the browser.
`fileSize` is signed into the canonical-headers section as `Content-Length`. The browser MUST echo the same Content-Length on its PUT (browsers compute this automatically from the request body length), and the storage gateway rejects any mismatch with SignatureDoesNotMatch. This is the server-side enforcement of the upload size cap on the presigned path: a caller cannot exceed the signed budget without invalidating the URL — closes the size-bypass security gap that the multipart `uploadFile` handler closes via `http.MaxBytesReader` + `MaxFileSize` on the request body. Pass a non-positive `fileSize` and the function returns an error rather than silently producing an unbounded URL.
type ServiceOSS ¶
func NewServiceOSS ¶
func NewServiceOSS(ctx *config.Context) *ServiceOSS
NewServiceOSS NewServiceOSS
func (*ServiceOSS) DownloadURL ¶
func (s *ServiceOSS) DownloadURL(path string, filename string) (string, error)
DownloadURL returns the public anonymous-GET URL for an object stored in the configured OSS bucket. The input `path` is the same shape that the file API at `modules/file/api.go` produces — `<fileType>/<...>` (e.g. `chat/2025/x.png`) — possibly with a leading slash.
The path is routed through `normalizeOSSObjectKey` before being joined with `BucketURL` so that the resulting URL points at the same OSS object the upload paths actually wrote: `UploadFile` and `PresignedPutURL` both strip a leading `<BucketName>/` segment to avoid double-bucketing the stored key. Without that normalization step here, the asymmetric case where the deployer's bucket name happens to equal a `fileType` prefix (e.g. `OSS.BucketName == "chat"`, `path == "chat/2025/x.png"` → object stored as `2025/x.png`) would emit a URL like `<BucketURL>/chat/2025/x.png` and 404. PR#50 R5 codex finding 2.4 closed this asymmetry on the upload side; lml2468 surfaced the surviving download-side mismatch in PR#50 R6.
The `filename` argument is preserved for API symmetry with other backends but is not used for OSS V1 download URLs (operators wanting per-request filename overrides should call `PresignedGetURL` instead, which embeds `response-content-disposition` in the signed URL).
func (*ServiceOSS) GetFile ¶
func (s *ServiceOSS) GetFile(path string) (io.ReadCloser, string, error)
func (*ServiceOSS) PresignedGetURL ¶
func (s *ServiceOSS) PresignedGetURL(objectPath string, filename string, disposition string, expires time.Duration) (string, error)
PresignedGetURL signs an OSS GET URL with a `response-content-disposition` override so the browser saves the file under the user-facing filename.
func (*ServiceOSS) PresignedPutURL ¶
func (s *ServiceOSS) PresignedPutURL(objectPath string, contentType string, contentDisposition string, fileSize int64, expires time.Duration) (uploadURL string, downloadURL string, err error)
PresignedPutURL signs an OSS PUT URL the browser can use directly. Aliyun OSS does NOT accept a separate Content-Disposition signature on PUT the way S3 does — disposition has to be embedded as object metadata at upload time. We therefore include it in the signed headers so the client echoes the same value, and the OSS gateway records it on the resulting object.
Caveat — Content-Length on OSS V1: the OSS V1 canonical-string algorithm does NOT cover Content-Length. Although we pass `oss.ContentLength` into SignURL the resulting signature is computed over Date / Content-Type / Canonicalized OSSHeaders / Resource — the size is advisory only and the OSS gateway accepts a PUT of any byte length under the signed URL. This is the OSS-side deviation from the SigV4 backends (MinIO/COS), where Content-Length IS folded into `X-Amz-SignedHeaders` and a wrong or missing value at PUT time returns 403 SignatureDoesNotMatch. To enforce a hard size budget on OSS, operators must rely on bucket / account-level policies (e.g. lifecycle quotas, RAM/STS policies that cap object size) — this server's signed URL alone cannot. We still require a positive `fileSize` so the request is well-formed and the API contract (`maxFileSize` echo) stays consistent across backends.
Caveat — Content-Disposition on OSS V1: same root cause. The OSS V1 canonical-string algorithm also does NOT include Content-Disposition, so a deviating value at PUT time does NOT produce SignatureDoesNotMatch the way it does on MinIO/COS. The browser-supplied Content-Disposition (or absence of one) is silently persisted. Operators who need strict disposition or size enforcement should migrate to a SigV4 backend (MinIO/COS), or run a post-upload validator. See `getUploadCredentials` docstring for the full per-header deviation matrix.
Roadmap — OSS V4 signing covers Content-Length canonically; switching the SDK call to `oss.WithSignVersion(oss.SignVersionV4)` would close this gap. Tracked separately so this PR can ship the customer-facing SigV4 fix without dragging the OSS SDK uplift along.
type ServiceQiniu ¶
func NewServiceQiniu ¶
func NewServiceQiniu(ctx *config.Context) *ServiceQiniu
NewServiceQiniu NewServiceQiniu
func (*ServiceQiniu) DownloadURL ¶
func (s *ServiceQiniu) DownloadURL(path string, filename string) (string, error)
func (*ServiceQiniu) GetFile ¶
func (s *ServiceQiniu) GetFile(path string) (io.ReadCloser, string, error)
func (*ServiceQiniu) PresignedGetURL ¶
func (s *ServiceQiniu) PresignedGetURL(objectPath string, filename string, disposition string, expires time.Duration) (string, error)
PresignedGetURL signs a private-bucket download URL for Qiniu, with the `attname` query parameter set so the browser saves under the user-facing filename.
func (*ServiceQiniu) PresignedPutURL ¶
func (s *ServiceQiniu) PresignedPutURL(objectPath string, contentType string, contentDisposition string, fileSize int64, expires time.Duration) (string, string, error)
PresignedPutURL is intentionally not implemented for Qiniu.
Qiniu's direct-upload contract is fundamentally different from the S3 presigned-PUT model that the rest of the IService surface assumes: a browser uploads to a fixed upload host (e.g. `up.qiniup.com`) using a multipart form whose `token` field carries an `UploadToken` derived from a `PutPolicy`. There is no single signed URL the browser can `PUT` to.
Surfacing that asymmetry as a clear error here keeps the IService signatures uniform across backends; deployments that need browser-direct upload to Qiniu should instead use the standard server-side UploadFile path or migrate to the COS / OSS / MinIO backends. The `configs/octo-server.yaml` support matrix documents this fallback.
type ServiceS3 ¶ added in v1.5.0
ServiceS3 implements a single-bucket S3 / S3-compatible storage backend.
Targets AWS S3 and any vendor that speaks the S3 protocol with SigV4 (Cloudflare R2, Backblaze B2, Wasabi, MinIO with a custom endpoint, ...). Configuration comes from cfg.S3 — see octo-lib config.S3Config for the field semantics and Viper bindings.
Design contrast with ServiceMinio / ServiceCOS:
- ServiceMinio routes by path prefix into N buckets and pins Region: us-east-1. ServiceS3 uses a single bucket from cfg.S3.Bucket and the explicit cfg.S3.Region; the upload type segment ("chat/", "moment/", ...) becomes part of the object key inside that one bucket.
- ServiceCOS bakes in cos.<region>.myqcloud.com endpoint templating plus a CDN-alias-vs-canonical client switch for path-style CDN fronts. ServiceS3 takes the endpoint verbatim from cfg.S3.Endpoint and supports UsePathStyle as an explicit operator opt-in.
Signing model: presigned PUT/GET URLs always sign against cfg.S3.Endpoint. cfg.S3.DownloadURL is the browser-facing prefix for UNSIGNED URLs only (preview redirects, upload response path) — it never participates in SigV4. This deliberate separation removes the previous host-shape detection on BucketURL and aligns with the octo-lib S3Config contract (see config.S3Config.DownloadURL godoc).
func NewServiceS3 ¶ added in v1.5.0
NewServiceS3 constructs a ServiceS3 from the active config. Required fields (Endpoint, Region, Bucket, AccessKeyID, SecretAccessKey) are NOT validated here — the constructor returns a usable struct even when fields are missing so the process can start (matching the existing ServiceMinio / ServiceCOS constructors), and per-request errors surface the missing configuration at the first call site. Operators should treat "S3 配置缺失" log lines as a startup failure.
func (*ServiceS3) DownloadURL ¶ added in v1.5.0
DownloadURL returns a browser-facing URL for the object. This is the *unsigned* URL — useful only when the bucket allows anonymous GET, or when fronted by a CDN that handles auth out-of-band. For private buckets (the AWS default with Block Public Access on), callers should use PresignedGetURL instead; modules/file/api.go's getFile handler redirects through DownloadURL but the public API also exposes /v1/file/download/url that goes through PresignedGetURL.
func (*ServiceS3) GetFile ¶ added in v1.5.0
GetFile fetches the object body for server-side handlers that need to proxy the file (e.g. legacy /v1/file/preview path). Buckets with Block Public Access turned on are still readable here because the SDK signs each request.
func (*ServiceS3) PresignedGetURL ¶ added in v1.5.0
func (s *ServiceS3) PresignedGetURL(objectPath string, filename string, disposition string, expires time.Duration) (string, error)
PresignedGetURL signs a GET URL with a Content-Disposition override so the browser downloads with the operator-supplied filename instead of the raw object key. Signing host is always cfg.S3.Endpoint (see file-level doc).
func (*ServiceS3) PresignedPutURL ¶ added in v1.5.0
func (s *ServiceS3) PresignedPutURL(objectPath string, contentType string, contentDisposition string, fileSize int64, expires time.Duration) (uploadURL string, downloadURL string, err error)
PresignedPutURL signs a direct-to-storage PUT URL. fileSize is signed into the canonical-headers section as Content-Length; any deviation the browser sends is rejected by the gateway with 403 SignatureDoesNotMatch. This is the server-side enforcement of MaxFileSize on the presigned path — same model as ServiceMinio / ServiceCOS, see modules/file/api.go getUploadCredentials for the full signed-header contract returned to clients.
Signing host is always cfg.S3.Endpoint; cfg.S3.DownloadURL has no influence on the signed URL. The returned downloadURL (unsigned) is constructed via publicURL and may carry the DownloadURL prefix.
func (*ServiceS3) UploadFile ¶ added in v1.5.0
func (s *ServiceS3) UploadFile(filePath string, contentType string, contentDisposition string, copyFileWriter func(io.Writer) error) (map[string]interface{}, error)
UploadFile streams a single object into the configured S3 bucket. The full filePath (e.g. "chat/2026/foo.jpg") becomes the object key after the optional cfg.S3.Prefix is prepended; the single-bucket model means the file-type segment lives in the key, not the bucket.
type Type ¶
type Type string
Type 文件类型
const ( // TypeChat 聊天文件 TypeChat Type = "chat" // TypeMoment 动态文件 TypeMoment Type = "moment" // TypeMomentCover 动态封面 TypeMomentCover Type = "momentcover" // TypeSticker 表情 TypeSticker Type = "sticker" // TypeReport 举报 TypeReport Type = "report" // TypeCommon 通用 TypeCommon Type = "common" // TypeChatBg 聊天背景 TypeChatBg Type = "chatbg" // TypeDownload 下载文件目录 TypeDownload = "download" // TypeWorkplaceBanner TypeWorkplaceBanner Type = "workplacebanner" // TypeWorkplaceAppIcon TypeWorkplaceAppIcon Type = "workplaceappicon" )