如何利用UNO将LibreOffice作为服务,解决Web应用文档并发转换失败问题
刚好在Elixir项目里解决过几乎一模一样的问题——之前用jodconverter-cli踩过自动停服务的坑,排队又损失并发,最后靠UNO服务模式完美解决了。下面给你一套可落地的步骤:
第一步:先把LibreOffice搭成常驻UNO服务
首先要让LibreOffice以无头服务模式启动,监听指定端口,这样它就不会每次转换就退出。执行这条shell命令:
libreoffice --headless --accept="socket,host=127.0.0.1,port=2002;urp;" --nofirststartwizard
参数解释:
--headless:无头运行,完全不启动GUI,适合服务器环境--accept:指定用TCP套接字+URP协议(UNO远程通信协议)监听本地2002端口,这是UNO服务的核心配置--nofirststartwizard:跳过首次启动的配置向导,避免服务启动时阻塞
建议把这个命令做成systemd服务(或者你用的服务器管理工具),设置开机自启、异常自动重启,确保服务一直可用。
第二步:在Elixir中调用UNO服务做转换
因为Elixir没有官方的UNO绑定,我们有两种实用的方式:
方式1:用LibreOffice自带的uno命令行工具(简单省心)
LibreOffice本身提供了uno CLI工具,可以直接向运行中的UNO服务发送转换请求,不需要重启服务。你可以在Elixir里通过System.cmd/3调用它:
def convert_doc_to_pdf(doc_path, pdf_path) do cmd = "uno" args = [ "-c", "com.sun.star.document.Converter", "--accept=socket,host=127.0.0.1,port=2002;urp;", "--in=#{Path.expand(doc_path)}", "--out=#{Path.expand(pdf_path)}", "--filter=writer_pdf_Export" ] case System.cmd(cmd, args, stderr_to_stdout: true) do {_output, 0} -> {:ok, pdf_path} {error, exit_code} -> {:error, "转换失败(代码#{exit_code}):#{error}"} end end
这个方案的优势是完全依赖官方工具,稳定性高,不用自己处理复杂的UNO通信细节,完美避开jodconverter-cli自动停服务的问题。
方式2:直接通过TCP套接字发送UNO RPC请求(适合复杂需求)
如果需要定制PDF导出参数(比如设置密码、压缩级别),可以直接用Elixir的:gen_tcp模块连接到2002端口,构造UNO的URP协议请求。不过这个门槛比较高,需要你熟悉UNO的API结构和URP协议格式。
简单来说步骤是:
- 用
:gen_tcp.connect/3建立到127.0.0.1:2002的连接 - 发送UNO的初始化握手数据包
- 构造调用
com.sun.star.document.Converter.convert方法的请求 - 解析服务端返回的响应
如果只是做基础的Doc转PDF,方式1完全够用,没必要折腾这个。
第三步:优化并发请求处理
现在LibreOffice服务可以处理并发请求了,但也要避免一次性压太多请求导致服务过载。你可以用Elixir的Task.Supervisor来做限流,控制同时处理的转换任务数:
defmodule DocConversion.Supervisor do use Task.Supervisor def start_link(_opts) do # 设置最大同时处理10个转换任务,可根据服务器配置调整 Task.Supervisor.start_link(name: __MODULE__, max_children: 10) end # 异步发起转换请求,不阻塞Web请求 def convert_async(doc_path, pdf_path) do Task.Supervisor.start_child(__MODULE__, fn -> convert_doc_to_pdf(doc_path, pdf_path) end) end end
这样既保留了并发能力,又不会让LibreOffice过载,用户等待时间也不会太长。
常见问题排查
- 连接不上UNO服务:检查LibreOffice进程是否在运行,端口2002是否被占用,本地防火墙是否允许回环连接
- 转换失败:给LibreOffice加
--log-file=/var/log/libreoffice-convert.log参数查看详细日志,或者在Elixir中捕获uno命令的输出排查错误 - 确保服务器安装了完整的LibreOffice包,包括Writer组件(否则无法处理Doc/Docx文件)
内容的提问来源于stack exchange,提问作者oldhomemovie

