convertersvc

package
v0.1.4 Latest Latest
Warning

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

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

Documentation

Overview

Package convertersvc 把 pkg/converter 的转换能力包成可被任务队列驱动的服务。

它解决的是 pkg/converter 留给调用方的三件事:输入从哪来、结果写到哪里、 以及"能不能安全地把调用方的输入变成转换库的输入"。

关键取舍是**不采信调用方给的文件名扩展名**。转换库按扩展名区分 docx/xlsx/pptx/ html——这些格式的魔数都是 ZIP 或纯文本,靠嗅探分不开。如果直接拿调用方的 "report.docx" 去转换,一个 .docx 内容配 .html 名字就会被送进 HTML 导入器, 既可能报错,也可能解析出完全错误的东西。因此这里先由服务端解析出可信的 输入格式,再用该格式的规范扩展名给临时文件命名,转换库的输入路径由我们生成。

Index

Constants

View Source
const (
	InputUpload = "upload"
	InputURL    = "url"
)

输入来源种类。

View Source
const (
	OutputStream = "stream"
	OutputDir    = "dir"
	// OutputFTP 直接上传到 FTP/FTPS 服务器。结果不落本地磁盘。
	OutputFTP = "ftp"
	// OutputS3 直接写入 S3 兼容的对象存储。
	OutputS3 = "s3"
	// OutputWebDAV 直接写入 WebDAV 共享。
	OutputWebDAV = "webdav"
	// OutputSFTP 直接写入 SFTP 服务器。
	OutputSFTP = "sftp"
)

输出目标种类。

View Source
const DefaultStreamLimit int64 = 64 << 20

DefaultStreamLimit 是 stream 输出的内存上限。

Variables

View Source
var (
	// ErrBadRequest 表示请求本身不合法,重试无意义。
	ErrBadRequest = errors.New("请求参数不合法")
	// ErrUnsupported 表示格式组合不支持,同样不该重试。
	ErrUnsupported = errors.New("不支持的格式组合")
	// ErrTooLarge 表示输入或输出超出限制。
	ErrTooLarge = errors.New("超出大小限制")
)

面向调用方的错误。

Functions

func IsHeavy

func IsHeavy(in Input, outputFormat string) bool

IsHeavy 报告该输入输出组合是否属于重通道。

判据有两类:一是会不会拉起外部进程(Office 走 LibreOffice、HTML 走 Chrome), 二是输入是不是要从网络拉——URL 输入的耗时由对端决定,不该占住快速通道。 这两类都慢在"不可预期"上,而 fast 通道的用途正是保证可预期的请求不被拖住。

单独导出成包级函数,是为了让 HTTP 层在入队时就能定下通道:通道写在任务 记录里供队列分派,事后改会让已入队的任务和实际执行的资源池对不上。

func NormalizeOutputKind

func NormalizeOutputKind(kind string) (string, error)

NormalizeOutputKind 把 output.kind 规范化为内部使用的标准值:空值按 stream 处理,忽略大小写与首尾空白,未知值报 ErrBadRequest。

导出是因为 HTTP 层必须在决定同步还是异步之前先做同一套判定。空值意味着 stream——若 HTTP 层用字面比较判断 `kind == "stream"`,省略字段的请求会被 送进异步队列,随后按 stream 处理、产物只留在内存里随 Result 丢弃:任务报 succeeded 而调用方拿不到任何东西。规范化放在这里而不是各写一份,是为了让 提交阶段与 worker 阶段永远得出同一个结论。

幂等:规范化后的值再规范化仍得到自身。

func ValidateOutputFileName

func ValidateOutputFileName(requested string) error

ValidateOutputFileName 供提交路径提前校验,让调用方拿到 400 而不是 一个注定失败的任务。

Types

type File

type File struct {
	Name string `json:"name"`
	Size int64  `json:"size"`
}

File 是目录输出里的一个文件。

type Input

type Input struct {
	// Kind 为 InputUpload 或 InputURL。
	Kind string `json:"kind"`
	// Format 可选的显式输入格式。为空时按文件名扩展名与魔数识别。
	Format string `json:"format,omitempty"`
	// FileName 是上传的原始文件名,仅用于识别格式,不直接用于转换。
	FileName string `json:"file_name,omitempty"`
	// Bytes 是 Kind 为 InputUpload 时的内容。
	Bytes []byte `json:"bytes,omitempty"`
	// URL 是 Kind 为 InputURL 时的地址,必须通过服务端白名单。
	URL string `json:"url,omitempty"`
}

Input 描述待转换文档的来源。

type Lane

type Lane string

Lane 是转换任务所属的执行通道,用于在任务队列里分配不同的并发额度。

const (
	// LaneFast 是纯 Go 的快速路径:OFD 解析与 PDF/文本/Markdown/图像输出。
	LaneFast Lane = "fast"
	// LaneHeavy 需要拉起外部进程(LibreOffice、Chrome),单次耗时与内存都高得多,
	// 必须与 LaneFast 分开限流,否则一批 Office 文档就能把服务打满。
	LaneHeavy Lane = "heavy"
)

type Output

type Output struct {
	// Kind 为 OutputStream、OutputDir,或某个远端目标类型。
	Kind string `json:"kind"`
	// Format 输出格式,如 "pdf"、"markdown"、"png"。
	Format string `json:"format"`
	// FileName 是产物文件名的基名,不含扩展名——扩展名由 Format 决定。
	//
	// 为什么给基名而不是完整文件名:扩展名本来就是格式决定的,让调用方也能
	// 指定就多了一处可能自相矛盾的地方(声明 pdf 却起名 .txt)。基名已经能
	// 表达调用方的意图(发票号、客户编号),又不与之冲突。
	//
	// 为空时用默认值:单文件是 "output",逐页输出是 "page"。
	//
	// 必须是单个路径组件,不含分隔符——否则就等于给了调用方一条穿越到
	// output_dir 之外的路径。完整路径仍由服务端决定(output_dir 之下、
	// 每个任务一个子目录),这一层隔离不受影响。
	FileName string `json:"file_name"`
	// Dir 是 Kind 为 OutputDir 时 output_dir 之下的相对子路径。
	Dir string `json:"dir"`
	// MaxStreamBytes 限制 stream 输出的内存占用,0 时取 DefaultStreamLimit。
	MaxStreamBytes int64 `json:"max_stream_bytes,omitempty"`
	// Remote 指向一个服务端预注册的远端目标,Kind 为远端类型时必填。
	Remote RemoteOutput `json:"remote,omitempty"`
}

Output 描述转换结果的落点。

type RemoteOutput

type RemoteOutput struct {
	// Target 是服务端配置里注册的目标名。调用方不能直接给地址与凭据——那等于
	// 让它往任意主机上传、往任意账号写数据。
	Target string `json:"target"`
	// Path 是该目标下的子路径,语义由目标自身的配置决定:FTP/SFTP/WebDAV 是
	// 远端 base_dir,S3 是桶内 prefix。调用方不必、也无法区分。
	Path string `json:"path,omitempty"`
}

RemoteOutput 描述远端落点。

四个协议共用一组字段,而不是每种协议一组(ftp_target/ftp_dir、 s3_target/s3_prefix、…):kind 已经表明用哪种协议,再按协议分字段是冗余的, 而且调用方无法从字段名判断该填哪个、填完产物落在哪。

type Result

type Result struct {
	// Kind 与 Spec.Output.Kind 一致。
	Kind string `json:"kind"`
	// InputFormat 实际判定的输入格式(已归一化),不是 Spec.Input.Format
	// 那个声明值。统计按类型分布必须用这个:用声明值会把"内容是 OFD、
	// 文件名是 .html"这类任务记成 ofd→pdf。
	InputFormat string `json:"input_format"`
	// InputBytes 实际读入的输入字节数,取落盘后文件的尺寸。
	// 输入一律先 materialize 成文件,所以这个值对上传、URL 与未来的
	// 其他来源口径一致。
	InputBytes int64 `json:"input_bytes"`
	// Format 实际使用的输出格式名(已归一化)。
	Format string `json:"format"`
	// MIME 输出的 MIME 类型。
	MIME string `json:"mime"`
	// FileName 输出文件名,stream 时有值。
	FileName string `json:"file_name,omitempty"`
	// Bytes stream 输出的内容。
	Bytes []byte `json:"-"`
	// Dir 目录输出的根路径。
	Dir string `json:"dir,omitempty"`
	// Files 目录输出里的文件清单。
	Files []File `json:"files,omitempty"`
	// TookMs 转换耗时(毫秒)。不用 time.Duration 直接序列化——那是纳秒。
	TookMs int64 `json:"took_ms"`
}

Result 是一次转换的结果。

type Service

type Service struct {
	// Allowlist 限定 input.kind=url 可以拉取的主机。为 nil 时 URL 输入一律拒绝。
	Allowlist *allowlist.List
	// TempDir 是转换期间的临时目录。转换器需要真实文件路径(LibreOffice、
	// Chrome 都如此),所以输入总是落盘一次。
	TempDir string
	// URLTimeout 是拉取远程输入的整体超时。
	URLTimeout time.Duration
	// DefaultOptions 是每个任务都会附加的可选项。
	DefaultOptions []converter.Option
	// FTPTargets 是服务端预注册的 FTP 目标,按名字索引。
	FTPTargets map[string]*transfer.FTPSink
	// S3Targets 是预注册的对象存储目标。
	S3Targets map[string]*transfer.MinioSink
	// WebDAVTargets 是预注册的 WebDAV 目标。
	WebDAVTargets map[string]*transfer.WebDAVSink
	// SFTPTargets 是预注册的 SFTP 目标。
	SFTPTargets map[string]*transfer.SFTPSink
}

Service 执行转换。

func New

func New(tempDir string, list *allowlist.List) *Service

New 构造服务。

func (*Service) Lane

func (s *Service) Lane(spec Spec) Lane

Lane 报告该任务应走哪条通道。

判据是"是否会拉起外部进程":Office 文档走 LibreOffice,HTML 输出走 Chrome, 以及两者之间的直接转换器。它们比纯 Go 路径慢一个数量级以上。

func (*Service) Run

func (s *Service) Run(ctx context.Context, spec Spec) (Result, error)

Run 执行一次转换。

type Spec

type Spec struct {
	Input  Input
	Output Output
	// Options 传给 pkg/converter 的可选项(DPI、纸张、外部程序路径等)。
	Options []converter.Option
}

Spec 是一次转换的完整描述。

Jump to

Keyboard shortcuts

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