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

convertersvc

package
v0.1.5 Latest Latest
Warning

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

Go to latest
Published: Oct 21, 2026 License: Apache-2.0 Imports: 21 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、XPS 走 xpstopdf), 二是输入是不是要从网络拉——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、xpstopdf),单次耗时与内存都高得多,
	// 必须与 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
	// contains filtered or unexported fields
}

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,XPS 走 xpstopdf, 以及两者之间的直接转换器。它们比纯 Go 路径慢一个数量级以上。

func (*Service) Run

func (s *Service) Run(ctx context.Context, spec Spec) (result Result, runErr 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