Docker Compose 安装 OnlyOffice
一、安装 OnlyOffice
使用 Docker Compose 安装 OnlyOffice 主要分为创建 docker-compose.yml 文件和准备配置文件两步。这里为你提供一个标准、可快速上手的配置方案。
1. 创建 docker-compose.yml 文件
首先,为你的项目创建一个目录(例如 onlyoffice),并在其中新建 docker-compose.yml 文件,填入以下内容:
services:
documentserver:
image: onlyoffice/documentserver:latest
container_name: onlyoffice_documentserver
restart: always
ports:
- "8888:80"
environment:
- JWT_SECRET=example # 请修改为强随机密钥
volumes:
- /vol1/1000/_docker/onlyoffice/logs:/var/log/onlyoffice
- /vol1/1000/_docker/onlyoffice/data:/var/www/onlyoffice/Data
- /vol1/1000/_docker/onlyoffice/lib:/var/lib/onlyoffice
- /vol1/1000/_docker/onlyoffice/db:/var/lib/postgresql
# 字体路径
- /vol1/1000/_docker/onlyoffice/fonts:/var/www/onlyoffice/documentserver/core-fonts/custom
说明:
volumes部分将容器内的目录映射到宿主机,这样配置、数据和日志都会保留在本地,便于管理。
2. 导入字体文件
将常用字体压缩包内的所有 .ttf 文件,解压到 docker-compose.yml 文件中配置映射字体路径 /vol1/1000/_docker/onlyoffice/fonts 下,用于配置需要的字体文件。
常用字体压缩包下载:
- 百度网盘链接: https://pan.baidu.com/s/1vfER4gVAmN-9964xQiv_8g?pwd=u2bz 提取码: u2bz
- 夸克网盘链接:https://pan.quark.cn/s/b5c8aac34eca?pwd=pVMe ;提取码:pVMe
已包含常用字体:
方正小标宋_GBK、仿宋_gb2312、黑体、Times New Roman等。
注意:仅支持 .ttf 后缀的全英文名称的字体文件(如“fangsong_gb2312.ttf”),不支持含有中文、后缀存在大写的字体文件(如“仿宋_gb2312.TTF”)。
3. 构建容器运行
docker-compose up -d
4. 设置字体文件权限并加载
-
进入容器命令行
/bin/sh。 -
检查字体文件权限,确保权限至少是
644(所有用户可读):ls -la /var/www/onlyoffice/documentserver/core-fonts/custom/*.ttf输出应类似:
-rw-r--r-- 1 root root 12345 Jul 13 ... kaitiGB2312.ttf如果权限不对,如:
$ ls -la /var/www/onlyoffice/documentserver/core-fonts/custom/*.ttf ---------- 1 ubuntu 1001 750216 Jul 13 04:33 /var/www/onlyoffice/documentserver/core-fonts/custom/arialbd.ttf ---------- 1 ubuntu 1001 562728 Jul 13 04:33 /var/www/onlyoffice/documentserver/core-fonts/custom/arialbi.ttf ---------- 1 ubuntu 1001 557004 Jul 13 04:33 /var/www/onlyoffice/documentserver/core-fonts/custom/ariali.ttf则修正权限:
chmod 644 /var/www/onlyoffice/documentserver/core-fonts/custom/*.ttf再次检查所有字体文件权限,应该显示:
$ ls -la /var/www/onlyoffice/documentserver/core-fonts/custom/*.ttf -rw-rw-r-- 1 ubuntu 1001 750216 Jul 13 04:33 /var/www/onlyoffice/documentserver/core-fonts/custom/arialbd.ttf -rw-rw-r-- 1 ubuntu 1001 562728 Jul 13 04:33 /var/www/onlyoffice/documentserver/core-fonts/custom/arialbi.ttf -rw-rw-r-- 1 ubuntu 1001 557004 Jul 13 04:33 /var/www/onlyoffice/documentserver/core-fonts/custom/ariali.ttf若有某个字体文件无法修改权限,则字体文件损坏,需要重新下载导入。如:
---------- 1 ubuntu 1001 557004 Jul 13 04:33 /var/www/onlyoffice/documentserver/core-fonts/custom/ariali.ttf -
强制刷新系统字体缓存:
fc-cache -f -v有时
fc-cache需要明确指定目录:fc-cache -f -v /var/www/onlyoffice/documentserver/core-fonts/custom/然后检查是否被识别(如
fangsong):fc-list | grep -i "kaiti\|fangsong" # 如果没有输出,则是字体文件没有复制或链接到系统标准字体目录 # cp /var/www/onlyoffice/documentserver/core-fonts/*.ttf /usr/share/fonts/truetype/ -
重新生成 OnlyOffice 字体索引:
/usr/bin/documentserver-generate-allfonts.sh观察输出,注意是否出现了
kaitiGB2312或fangsongGB2312的字样。如果没有任何错误,脚本会生成新的字体列表。 -
重启 OnlyOffice 服务。
supervisorctl restart all
5. 验证字体加载情况
有两个位置:
/var/www/onlyoffice/documentserver/sdkjs/common/AllFonts.js- 前端使用的字体列表/var/www/onlyoffice/documentserver/server/FileConverter/bin/AllFonts.js- 转换器使用的字体列表
查询配置内容:
grep -i "kaiti\|fangsong\|gb2312" /var/www/onlyoffice/documentserver/sdkjs/common/AllFonts.js
grep -i "kaiti\|fangsong\|gb2312" /var/www/onlyoffice/documentserver/server/FileConverter/bin/AllFonts.js
示例:
$ grep -i "kaiti\|fangsong\|gb2312" /var/www/onlyoffice/documentserver/sdkjs/common/AllFonts.js
["仿宋_GB2312",79,0,-1,-1,-1,-1,-1,-1],
["楷体_GB2312",82,0,-1,-1,-1,-1,-1,-1],
$ grep -i "kaiti\|fangsong\|gb2312" /var/www/onlyoffice/documentserver/server/FileConverter/bin/AllFonts.js
"/var/www/onlyoffice/documentserver/core-fonts/custom/fangsongGB2312.ttf",
"/var/www/onlyoffice/documentserver/core-fonts/custom/kaitiGB2312.ttf",
["仿宋_GB2312",79,0,-1,-1,-1,-1,-1,-1],
["楷体_GB2312",82,0,-1,-1,-1,-1,-1,-1],
6. 修复 HTTPS 连接验证报错
当我使用 动态转换工具 调用 OnlyOffice 进行 .docx 转换为 .pdf 操作时,报错:
[2026-07-30 00:22:44] [INFO] Process_P - [N/A.convert] OnlyOffice 发起转换: http://192.168.1.7:10371/ConvertService.ashx , 源文件: https://192.168.1.8:10370/work/dynamic/php/depend/temp/convert_1785342164_aa1e735f.docx
[2026-07-30 00:22:48] [ERROR] Process_P - [N/A.main] OnlyOffice 转换失败 (error -4): Document Server 下载源文件失败(请检查回源地址是否可被 OnlyOffice 服务器访问)
[2026-07-30 00:22:48] [ERROR] Process_P - [N/A.main] 所有转换引擎均失败: OnlyOffice 转换失败 (error -4): Document Server 下载源文件失败(请检查回源地址是否可被 OnlyOffice 服务器访问)
[2026-07-30 00:22:48] [ERROR] MainProcess - [MainProcess._log_response] TransWordToPDF 执行失败:
{'OnlyOffice 转换失败 (error -4): Document Server 下载源文件失败(请检查回源地址是否可被 OnlyOffice 服务器访问)'}
分析:
-
对于 OnlyOffice Document Server的 ConvertService.ashx 官方只支持URL方式,接受JSON(其中url字段指定可访问的源文件URL),不支持直接POST文件上传。
我设计的 动态转换工具 调用 OnlyOffice 的逻辑,是向 OnlyOffice 发送 动态转换工具 生产服务器 NGINX 提供的局域网 HTTPS 网址,故而生产服务器实际的 SSL 证书(公网域名)与实际发送的局域网 HTTPS 网址域名不符,导致证书判定异常。
方案 A:让 OnlyOffice 跳过证书校验
改动最小,推荐先用这个。
在 192.168.1.7 上:
docker exec <onlyoffice容器名> sed -i 's/"rejectUnauthorized": true/"rejectUnauthorized": false/' /etc/onlyoffice/documentserver/default.json
docker restart <onlyoffice容器名>
config.ini 不用改,重试转换即可。注意:容器重建(docker compose down/up)后此修改会丢失,需重新执行或把 default.json 挂载出来。
方案 B:回源用域名走正常证书校验
更干净,前提是 work.yogile.site 证书是正规 CA 签发。
-
让 OnlyOffice 容器能把域名解析到 192.168.1.8 —— 在 docker-compose.yml 的服务下加:
extra_hosts: - "work.yogile.site:192.168.1.8"然后
docker compose up -d重建。 -
生产 config.ini 改为:
onlyoffice_public_base_url = https://work.yogile.site:10370/work/dynamic/php/depend/temp
如果证书其实是自签的,方案 B 无效,直接用方案 A。
OnlyOffice 有一个“文档存储服务”的API,通常需要配合文档管理服务(如example)使用。但官方示例服务主要为演示和开发而设计,但其暴露的 API 端点历史上存在多个高危漏洞,在生产环境中启用它会为系统引入不必要的攻击面。
7. 验证 OnlyOffice 服务运行情况
test.py 测试文件:
"""
OnlyOffice 文档转换工具(直接调用内置 ConvertService API,无需 example 服务)
功能:通过 OnlyOffice Document Server 的官方转换接口 ConvertService.ashx,
将本地 docx 文件转换为 PDF 并下载到指定文件夹。
容器启动后即可使用,无需执行 supervisorctl start ds:example。
原理说明:
ConvertService.ashx 不接受文件上传,而是要求提供一个可访问的 URL,
由 Document Server 主动下载源文件。因此本脚本:
1. 在本机临时启动一个 HTTP 服务(随机端口,仅暴露待转换的单个文件)
2. POST /ConvertService.ashx(JSON + JWT 签名,async=false 同步等待)
3. 从响应的 fileUrl 下载转换后的 PDF
4. 关闭本地 HTTP 服务
转换结果只存在于 Document Server 的临时缓存中(自动过期清理),
不会像 example 服务那样持久化到存储目录,因此无需手动清理服务器文件。
使用方式:
python test.py # 使用默认路径
python test.py <docx文件路径> # 指定输入文件
python test.py <docx文件路径> <输出目录> # 指定输入输出
依赖安装:
pip install requests pyjwt
注意事项:
- 本机防火墙需允许 OnlyOffice 服务器(192.168.1.7)访问临时端口的入站连接,
否则 Document Server 无法下载源文件(报错 error -4)。
- JWT_SECRET 必须与 docker-compose.yml 中的 JWT_SECRET 一致。
"""
import sys
import time
import uuid
import socket
import threading
from pathlib import Path
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import requests
import jwt # pip install PyJWT
# ==================== 配置 ====================
# OnlyOffice 容器所在服务器IPv4地址
ONLYOFFICE_HOST = "192.168.1.7"
# OnlyOffice 容器所在服务器对外服务端口
ONLYOFFICE_PORT = 10371 # 留空("" 或 None)时 URL 不拼接端口,使用协议默认端口
# OnlyOffice 容器所在服务器对外服务协议
ONLYOFFICE_PROTOCOL = "http" # 仅允许 "http" 或 "https"
assert ONLYOFFICE_PROTOCOL in ("http", "https"), (
f"ONLYOFFICE_PROTOCOL 必须为 \"http\" 或 \"https\",当前值为: {ONLYOFFICE_PROTOCOL!r}"
)
_ONLYOFFICE_PORT_PART = f":{ONLYOFFICE_PORT}" if ONLYOFFICE_PORT not in ("", None) else ""
# OnlyOffice 容器所在服务器对外服务网址(自动拼接)
ONLYOFFICE_URL = f"{ONLYOFFICE_PROTOCOL}://{ONLYOFFICE_HOST}{_ONLYOFFICE_PORT_PART}"
# JWT 密钥(必须与 docker-compose.yml 中的 JWT_SECRET 一致)
JWT_SECRET = "example"
# 项目根目录
PROJECT_ROOT = Path(__file__).resolve().parent
# 默认输入文件,文件必须与 test.py 在同一文件夹下(或另行指定DEFAULT_DOCX_PATH变量)
DEFAULT_DOCX_PATH = str(
PROJECT_ROOT / "测试文件.docx"
)
# 默认输出目录,输出文件夹与 test.py 在同一文件夹下
DEFAULT_OUTPUT_DIR = str(PROJECT_ROOT / "输出")
# ConvertService 错误码对照表(官方文档定义)
CONVERT_ERRORS = {
-1: "未知错误",
-2: "转换超时",
-3: "转换出错",
-4: "Document Server 下载源文件失败(请检查本机防火墙是否放行临时端口)",
-5: "文档密码不正确",
-6: "访问转换数据库出错",
-7: "请求参数错误",
-8: "JWT 令牌无效(请检查 JWT_SECRET 是否与 docker-compose.yml 一致)",
-9: "无法自动确定输出格式",
-10: "文件大小超出限制",
}
# ==================== 本地临时文件服务器 ====================
def _get_local_ip():
"""
获取本机在局域网中的 IP(即 OnlyOffice 服务器可访问到的地址)。
通过向 OnlyOffice 主机发起 UDP "连接"(不实际发包)获取本机出口 IP,
避免多网卡环境下取错地址。
Returns:
str: 本机局域网 IP
"""
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
s.connect((ONLYOFFICE_HOST, 1))
return s.getsockname()[0]
finally:
s.close()
class _SingleFileServer:
"""
只暴露单个文件的临时 HTTP 服务器。
OnlyOffice ConvertService 需要通过 URL 下载源文件,
该服务器在随机端口上仅响应指定路径的 GET 请求,其余一律 404。
"""
def __init__(self, file_path):
"""
Args:
file_path: 待暴露的本地文件路径
"""
file_bytes = Path(file_path).read_bytes()
# ASCII 安全的 URL 路径,避免中文文件名编码问题
url_name = f"convert_{int(time.time())}_{uuid.uuid4().hex[:6]}.docx"
expected_path = f"/{url_name}"
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path != expected_path:
self.send_error(404)
return
self.send_response(200)
self.send_header("Content-Type", "application/octet-stream")
self.send_header("Content-Length", str(len(file_bytes)))
self.end_headers()
self.wfile.write(file_bytes)
def log_message(self, fmt, *args):
# 静默访问日志
pass
# 绑定 0.0.0.0 随机端口
self._server = ThreadingHTTPServer(("0.0.0.0", 0), Handler)
self.port = self._server.server_address[1]
self.file_url = f"http://{_get_local_ip()}:{self.port}{expected_path}"
self._thread = threading.Thread(
target=self._server.serve_forever, daemon=True
)
def start(self):
"""启动服务线程"""
self._thread.start()
def stop(self):
"""关闭服务器并释放端口"""
self._server.shutdown()
self._server.server_close()
# ==================== JWT 生成 ====================
def _sign_request(body):
"""
为 ConvertService 请求生成 JWT。
官方要求:
- body 中的 token 字段:直接以请求参数为 payload 签名
- Authorization 请求头:以 {"payload": <请求参数>} 为 payload 签名
Args:
body: 请求参数 dict(不含 token 字段)
Returns:
tuple[str, str]: (body 内嵌 token, Authorization 头 token)
"""
body_token = jwt.encode(body, JWT_SECRET, algorithm="HS256")
header_token = jwt.encode({"payload": body}, JWT_SECRET, algorithm="HS256")
return body_token, header_token
# ==================== 文档转换核心逻辑 ====================
def convert_docx_to_pdf(docx_path, output_dir):
"""
通过 OnlyOffice ConvertService.ashx 将 docx 文件转换为 PDF。
执行流程:
1. 验证输入文件
2. 启动本地临时 HTTP 服务暴露源文件
3. POST /ConvertService.ashx 发起同步转换
4. 从响应的 fileUrl 下载 PDF
Args:
docx_path: 输入的 docx 文件路径
output_dir: PDF 输出目录路径
Returns:
str: 输出 PDF 文件的绝对路径
Raises:
FileNotFoundError: 输入文件不存在
ConnectionError: 无法连接 OnlyOffice 服务
Exception: 转换/下载失败
"""
docx_path = Path(docx_path).resolve()
output_dir = Path(output_dir).resolve()
# ---- 步骤 1:验证输入 ----
if not docx_path.exists():
raise FileNotFoundError(f"输入文件不存在: {docx_path}")
output_dir.mkdir(parents=True, exist_ok=True)
output_pdf = output_dir / f"{docx_path.stem}.pdf"
print(f" [1/4] 输入文件: {docx_path.name} ({docx_path.stat().st_size:,} 字节)")
# ---- 步骤 2:启动本地临时文件服务 ----
file_server = _SingleFileServer(docx_path)
file_server.start()
print(f" [2/4] 本地临时文件服务已启动: {file_server.file_url}")
try:
# ---- 步骤 3:发起转换 ----
file_key = f"convert_{int(time.time())}_{uuid.uuid4().hex[:8]}"
body = {
"async": False,
"filetype": docx_path.suffix.lstrip(".").lower(),
"key": file_key,
"outputtype": "pdf",
"title": f"{docx_path.stem}.pdf",
"url": file_server.file_url,
}
body_token, header_token = _sign_request(body)
body["token"] = body_token
print(f" [3/4] 调用 ConvertService.ashx (docx → pdf) ...")
try:
resp = requests.post(
f"{ONLYOFFICE_URL}/ConvertService.ashx",
json=body,
headers={
"Accept": "application/json",
"Authorization": f"Bearer {header_token}",
},
timeout=180,
)
except requests.exceptions.ConnectionError:
raise ConnectionError(
f"无法连接到 OnlyOffice 服务 ({ONLYOFFICE_URL})\n"
f"请确认:\n"
f" 1. Docker 容器是否正在运行\n"
f" 2. 防火墙是否允许端口 {ONLYOFFICE_PORT}"
)
except requests.exceptions.RequestException as e:
raise Exception(f"转换请求失败: {e}")
if resp.status_code != 200:
raise Exception(
f"转换失败 (HTTP {resp.status_code}): {resp.text[:300]}"
)
try:
result = resp.json()
except Exception:
raise Exception(f"转换响应解析失败: {resp.text[:300]}")
# 检查错误码
if "error" in result:
code = result["error"]
raise Exception(
f"转换失败 (error {code}): {CONVERT_ERRORS.get(code, '未知错误码')}"
)
if not result.get("endConvert"):
raise Exception(f"转换未完成: {result}")
file_url = result.get("fileUrl")
if not file_url:
raise Exception(f"转换完成但响应中未包含 fileUrl: {result}")
print(f" 转换完成,结果地址: {file_url}")
# ---- 步骤 4:下载 PDF ----
print(f" [4/4] 下载 PDF 到本地...")
try:
download_resp = requests.get(file_url, timeout=120)
download_resp.raise_for_status()
except requests.exceptions.RequestException as e:
raise Exception(f"PDF 下载失败: {e}")
with open(output_pdf, "wb") as f:
f.write(download_resp.content)
file_size = output_pdf.stat().st_size
print(f" PDF 已保存: {output_pdf}")
print(f" 文件大小: {file_size:,} 字节")
return str(output_pdf)
finally:
# 无论成败,关闭本地临时文件服务
file_server.stop()
# 转换结果仅存于 Document Server 临时缓存,会自动过期清理,
# 无需像 example 服务那样手动删除服务器文件。
# ==================== 命令行入口 ====================
def main():
"""命令行主入口函数"""
# Windows GBK 控制台无法打印 ✓/✗ 等符号,统一切换为 UTF-8 输出
try:
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
except AttributeError:
pass
print("=" * 60)
print(" OnlyOffice 文档转换工具 (直接 API,无需 example 服务)")
print("=" * 60)
# 解析参数
if len(sys.argv) >= 2:
docx_path = sys.argv[1]
else:
docx_path = DEFAULT_DOCX_PATH
if len(sys.argv) >= 3:
output_dir = sys.argv[2]
else:
output_dir = DEFAULT_OUTPUT_DIR
# 显示配置
print(f" OnlyOffice 服务 : {ONLYOFFICE_URL}")
print(f" 输入文件 : {docx_path}")
print(f" 输出目录 : {output_dir}")
print("-" * 60)
# 检查依赖
try:
import requests # noqa: F401, F811
import jwt # noqa: F401, F811
except ImportError as e:
print(f"✗ 缺少依赖包: {e}")
print("请安装: pip install requests pyjwt")
return 4
# 执行转换
try:
result_path = convert_docx_to_pdf(docx_path, output_dir)
print("-" * 60)
print(f"✓ 转换成功!输出文件: {result_path}")
return 0
except FileNotFoundError as e:
print(f"\n✗ 文件错误: {e}")
return 1
except ConnectionError as e:
print(f"\n✗ 连接错误: {e}")
return 2
except Exception as e:
print(f"\n✗ 转换失败: {e}")
return 3
if __name__ == "__main__":
sys.exit(main())

浙公网安备 33010602011771号