目录

Golang+LibreOffice6.2 实战:Word/Excel/PPTX 转 PDF/HTML

最近遇到一个核心需求:用户上传的 Word(.doc/.docx)、Excel(.xls/.xlsx)、PPTX 文件,需要支持在线预览。

调研后发现,最稳妥的方案是先将这些文档转成 PDF(适合正式预览)或 HTML(适合轻量预览),

而 LibreOffice 作为免费开源的办公套件,不仅支持多格式转换,还能通过命令行调用,完美适配 Golang 的集成需求。

本文基于 LibreOffice 6.2 版本(稳定性强、兼容性好,实测支持 Office 2003-2019 格式),结合 Golang 的exec包封装转换逻辑,

记录分享从环境搭建到代码实现,再到中文乱码、转换超时等坑点解决,希望对大家有所帮助。

一、核心原理

Golang + LibreOffice 转换的实现逻辑:

  1. LibreOffice 转换能力:LibreOffice 内置文档格式解析引擎,支持通过命令行工具 soffice(Linux/Mac)或 soffice.exe(Windows)执行无界面转换,无需打开 GUI 窗口。
  2. Golang 调用方式:Golang 通过 os/exec 包执行系统命令,调用 LibreOffice 的转换指令,捕获执行结果(成功 / 失败)和输出日志,实现程序层面的自动化转换。
  3. 转换流程:用户上传文档 → Golang 接收文件并保存到临时目录 → 调用 LibreOffice 命令行转换 → 读取转换后的 PDF/HTML 文件 → 返回给前端预览。

二、环境准备

安装 LibreOffice6.2 并配置

以下是 Linux(CentOS/Ubuntu)和 Windows 的详细步骤:

(一)linux 系统

以 Centos 7 为例

直接下载

# CentOS 7(64位)示例:下载RPM包
wget https://downloadarchive.documentfoundation.org/libreoffice/old/6.2.0/rpm/x86_64/LibreOffice_6.2.0_Linux_x86-64_rpm.tar.gz

# Ubuntu 18.04(64位)示例:下载DEB包
wget https://downloadarchive.documentfoundation.org/libreoffice/old/6.2.0/deb/x86_64/LibreOffice_6.2.0_Linux_x86-64_deb.tar.gz

⚠️地址可能失效,请以官网最新地址为准:下载 LibreOffice | LibreOffice 简体中文官方网站 - 自由免费的办公套件

解压并安装

# CentOS 7安装步骤
tar -zxvf LibreOffice_6.2.0_Linux_x86-64_rpm.tar.gz
cd LibreOffice_6.2.0.3_Linux_x86-64_rpm/RPMS/
# 安装所有必需组件(含Writer/Calc/Impress,对应Word/Excel/PPTX)
yum localinstall *.rpm -y

# Ubuntu 18.04安装步骤
tar -zxvf LibreOffice_6.2.0_Linux_x86-64_deb.tar.gz
cd LibreOffice_6.2.0.3_Linux_x86-64_deb/DEBS/
dpkg -i *.deb
# 修复依赖缺失
apt -f install -y

安装依赖组件

关键!避免转换失败

LibreOffice 转换需要 Java 运行环境和字体支持,必须安装:

# CentOS 7
yum install java-1.8.0-openjdk fontconfig -y

# Ubuntu 18.04
apt install openjdk-8-jre fontconfig -y

验证安装

# 查看LibreOffice版本(输出6.2.0即成功)
soffice --version

# 测试命令行转换(转换一个测试Word文件到PDF)
soffice --headless --invisible --nodefault --norestore --nofirststartwizard --convert-to pdf --outdir /tmp /path/to/test.docx

(二)windows 系统

  • 下载

    官网下载 Windows 版本:下载 LibreOffice | LibreOffice 简体中文官方网站 - 自由免费的办公套件

  • 安装

    • 安装路径建议默认(C:\Program Files\LibreOffice 6.2\),方便后续调用。
    • 安装时勾选 “Writer”“Calc”“Impress” 组件(默认已勾选,无需修改)。
    • 安装完成后,将 C:\Program Files\LibreOffice 6.2\program 加入系统环境变量 PATH,确保soffice.exe可全局调用。
  • 验证安装

    打开 CMD 命令行,执行:

    soffice --version

    输出 “LibreOffice 6.2.0.3” 即成功。

三、Go 实现转换

核心代码如下:

package main

import (
    "errors"
    "fmt"
    "log"
    "os"
    "os/exec"
    "path/filepath"
    "runtime"
    "strings"
    "time"
)

// ConvertDoc 文档转换核心函数
// inputPath: 输入文件路径(如./test.docx)
// outputDir: 输出目录(如./output)
// targetType: 目标格式(pdf/html)
// 返回值: 输出文件路径、错误信息
func ConvertDoc(inputPath, outputDir, targetType string) (string, error) {
    // 1. 校验参数
    if inputPath == "" {
        return "", errors.New("输入文件路径不能为空")
    }
    if outputDir == "" {
        outputDir = "./output"
    }
    if targetType != "pdf" && targetType != "html" {
        return "", errors.New("目标格式仅支持pdf和html")
    }

    // 2. 检查输入文件是否存在
    if _, err := os.Stat(inputPath); os.IsNotExist(err) {
        return "", fmt.Errorf("输入文件不存在:%s", inputPath)
    }

    // 3. 创建输出目录(不存在则创建)
    if err := os.MkdirAll(outputDir, 0755); err != nil {
        return "", fmt.Errorf("创建输出目录失败:%v", err)
    }

    // 4. 拼接LibreOffice命令(区分Windows和Linux)
    var cmd *exec.Cmd
    sofficePath := "soffice" // Linux/Mac默认命令
    if runtime.GOOS == "windows" {
        // Windows默认安装路径(可根据实际安装路径修改)
        sofficePath = `C:\Program Files\LibreOffice 6.2\program\soffice.exe`
        // 检查soffice.exe是否存在
        if _, err := os.Stat(sofficePath); os.IsNotExist(err) {
            return "", errors.New("Windows系统未找到soffice.exe,请检查LibreOffice安装路径")
        }
    }

    // 转换参数说明:
    // --headless:无界面模式(必须)
    // --invisible:隐藏窗口(避免弹窗)
    // --nodefault:不加载默认文档
    // --norestore:不恢复之前的文档
    // --nofirststartwizard:不显示首次启动向导
    // --convert-to:指定目标格式(pdf/html)
    // --outdir:指定输出目录
    args := []string{
        "--headless",
        "--invisible",
        "--nodefault",
        "--norestore",
        "--nofirststartwizard",
        fmt.Sprintf("--convert-to=%s", targetType),
        fmt.Sprintf("--outdir=%s", outputDir),
        inputPath,
    }

    cmd = exec.Command(sofficePath, args...)

    // 5. 捕获命令输出(方便调试)
    var output strings.Builder
    cmd.Stdout = &output
    cmd.Stderr = &output

    // 6. 设置命令执行超时(避免长时间卡住,默认30秒)
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()
    cmd = cmd.WithContext(ctx)

    // 7. 执行命令
    log.Printf("开始转换:输入文件=%s,目标格式=%s", inputPath, targetType)
    if err := cmd.Run(); err != nil {
        return "", fmt.Errorf("转换失败:%v,命令输出:%s", err, output.String())
    }

    // 8. 拼接输出文件路径(默认与输入文件同名,后缀为targetType)
    inputFileName := filepath.Base(inputPath)
    outputFileName := strings.TrimSuffix(inputFileName, filepath.Ext(inputFileName)) + "." + targetType
    outputPath := filepath.Join(outputDir, outputFileName)

    // 9. 检查输出文件是否存在(避免转换成功但文件缺失)
    if _, err := os.Stat(outputPath); os.IsNotExist(err) {
        return "", fmt.Errorf("转换命令执行成功,但未找到输出文件:%s", outputPath)
    }

    log.Printf("转换成功:输出文件=%s", outputPath)
    return outputPath, nil
}

调用测试:

package main

import (
    "context"
    "fmt"
    "log"
)

func main() {
    // 测试参数(根据实际文件路径修改)
    inputDoc := "./test.docx"   // Word文件
    inputExcel := "./test.xlsx" // Excel文件
    inputPPTX := "./test.pptx"  // PPTX文件
    outputDir := "./output"     // 输出目录

    // 1. Word转PDF
    pdfPath, err := ConvertDoc(inputDoc, outputDir, "pdf")
    if err != nil {
        log.Printf("Word转PDF失败:%v", err)
    } else {
        fmt.Printf("Word转PDF成功:%s\n", pdfPath)
    }

    // 2. Excel转HTML
    htmlPath, err := ConvertDoc(inputExcel, outputDir, "html")
    if err != nil {
        log.Printf("Excel转HTML失败:%v", err)
    } else {
        fmt.Printf("Excel转HTML成功:%s\n", htmlPath)
    }

    // 3. PPTX转PDF
    pptxPdfPath, err := ConvertDoc(inputPPTX, outputDir, "pdf")
    if err != nil {
        log.Printf("PPTX转PDF失败:%v", err)
    } else {
        fmt.Printf("PPTX转PDF成功:%s\n", pptxPdfPath)
    }
}

执行可以看到转换成功

常见问题

Q1. 中文乱码解决方案

LibreOffice 默认缺少中文字体,转换中文文档时会出现乱码,需手动安装中文字体:

linux 安装字体示例

# 1. 安装Windows常用中文字体(宋体、黑体等)
yum install wqy-microhei-fonts wqy-zenhei-fonts -y

# 2. 刷新字体缓存
fc-cache -fv

# 3. 验证字体是否生效
fc-list | grep "WenQuanYi"

Q2. 转换超时?

如果转换大文件(如 100MB + 的 Excel),30 秒超时可能不够,可在ConvertDoc函数中增加超时参数配置:

// 优化后的函数参数:增加timeout参数
func ConvertDoc(inputPath, outputDir, targetType string, timeout time.Duration) (string, error) {
    // ... 其他代码不变 ...
    // 超时默认30秒,支持自定义
    if timeout <= 0 {
        timeout = 30 * time.Second
    }
    ctx, cancel := context.WithTimeout(context.Background(), timeout)
    // ...
}

Q3. 转换失败:报错 “Headless mode is not supported on Windows”

Windows 系统下 LibreOffice 6.2 的--headless参数兼容性问题,部分环境会提示不支持。

移除--headless参数,仅保留--invisible,修改 args 为

args := []string{
    "--invisible",
    "--nodefault",
    "--norestore",
    "--nofirststartwizard",
    fmt.Sprintf("--convert-to=%s", targetType),
    fmt.Sprintf("--outdir=%s", outputDir),
    inputPath,
}

总结

Golang + LibreOffice 6.2 是实现办公文档转 PDF/HTML 的高效方案,核心优势在于:

  1. 免费开源:无需依赖付费 SDK 或 API,降低成本;
  2. 跨平台支持:一套代码适配 Linux/Windows 服务器;
  3. 格式兼容性强:支持 Office 2003-2019 全格式,转换效果稳定;
  4. 集成简单:通过命令行调用,Golang 只需几行代码即可封装。

需要注意的是,生产环境使用时需做好这几点:

  • 提前安装中文字体,避免乱码;
  • 根据文件大小合理设置超时时间;
  • 对输入文件进行格式校验,避免非法文件导致转换失败;
  • 定期清理临时转换文件,节省磁盘空间。

如果你的场景需要处理更复杂的文档(如带宏的 Office 文件、特殊格式表格),或需要更高的转换速度,欢迎留言讨论,我会分享进一步的优化方案!

版权声明

未经授权,禁止转载本文章。
如需转载请保留原文链接并注明出处。即视为默认获得授权。
未保留原文链接未注明出处或删除链接将视为侵权,必追究法律责任!

本文原文链接: https://fiveyoboy.com/articles/go-libreoffice-convert/

备用原文链接: https://blog.fiveyoboy.com/articles/go-libreoffice-convert/