Documentation
¶
Overview ¶
Package convertersvc 把 pkg/converter 的转换能力包成可被任务队列驱动的服务。
它解决的是 pkg/converter 留给调用方的三件事:输入从哪来、结果写到哪里、 以及"能不能安全地把调用方的输入变成转换库的输入"。
关键取舍是**不采信调用方给的文件名扩展名**。转换库按扩展名区分 docx/xlsx/pptx/ html——这些格式的魔数都是 ZIP 或纯文本,靠嗅探分不开。如果直接拿调用方的 "report.docx" 去转换,一个 .docx 内容配 .html 名字就会被送进 HTML 导入器, 既可能报错,也可能解析出完全错误的东西。因此这里先由服务端解析出可信的 输入格式,再用该格式的规范扩展名给临时文件命名,转换库的输入路径由我们生成。
Index ¶
Constants ¶
const ( InputUpload = "upload" InputURL = "url" )
输入来源种类。
const ( OutputStream = "stream" OutputDir = "dir" // OutputFTP 直接上传到 FTP/FTPS 服务器。结果不落本地磁盘。 OutputFTP = "ftp" // OutputS3 直接写入 S3 兼容的对象存储。 OutputS3 = "s3" // OutputWebDAV 直接写入 WebDAV 共享。 OutputWebDAV = "webdav" // OutputSFTP 直接写入 SFTP 服务器。 OutputSFTP = "sftp" )
输出目标种类。
const DefaultStreamLimit int64 = 64 << 20
DefaultStreamLimit 是 stream 输出的内存上限。
Variables ¶
var ( // ErrBadRequest 表示请求本身不合法,重试无意义。 ErrBadRequest = errors.New("请求参数不合法") // ErrUnsupported 表示格式组合不支持,同样不该重试。 ErrUnsupported = errors.New("不支持的格式组合") // ErrTooLarge 表示输入或输出超出限制。 ErrTooLarge = errors.New("超出大小限制") )
面向调用方的错误。
Functions ¶
func IsHeavy ¶
IsHeavy 报告该输入输出组合是否属于重通道。
判据有两类:一是会不会拉起外部进程(Office 走 LibreOffice、HTML 走 Chrome), 二是输入是不是要从网络拉——URL 输入的耗时由对端决定,不该占住快速通道。 这两类都慢在"不可预期"上,而 fast 通道的用途正是保证可预期的请求不被拖住。
单独导出成包级函数,是为了让 HTTP 层在入队时就能定下通道:通道写在任务 记录里供队列分派,事后改会让已入队的任务和实际执行的资源池对不上。
func NormalizeOutputKind ¶
NormalizeOutputKind 把 output.kind 规范化为内部使用的标准值:空值按 stream 处理,忽略大小写与首尾空白,未知值报 ErrBadRequest。
导出是因为 HTTP 层必须在决定同步还是异步之前先做同一套判定。空值意味着 stream——若 HTTP 层用字面比较判断 `kind == "stream"`,省略字段的请求会被 送进异步队列,随后按 stream 处理、产物只留在内存里随 Result 丢弃:任务报 succeeded 而调用方拿不到任何东西。规范化放在这里而不是各写一份,是为了让 提交阶段与 worker 阶段永远得出同一个结论。
幂等:规范化后的值再规范化仍得到自身。
func ValidateOutputFileName ¶
ValidateOutputFileName 供提交路径提前校验,让调用方拿到 400 而不是 一个注定失败的任务。
Types ¶
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 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 执行转换。