找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

224

积分

0

好友

27

主题
发表于 前天 14:52 | 查看: 10| 回复: 0

前言

在许多项目中,会遇到需要在前端页面直接编辑Word文档并保存的需求。经过调研,我们选择了功能强大的开源组件OnlyOffice来实现在线编辑功能。OnlyOffice不仅支持文档编辑,还具备文档格式转换、多人协同编辑和打印等功能,本文主要聚焦于文档编辑与保存的核心实现。

1、OnlyOffice的部署

部署OnlyOffice主要有两种方式:Docker部署和本地直接安装。相比之下,Docker部署更为简便,只需拉取镜像并配置启动参数即可。由于最初搜索到的是Linux本地部署方案,本文采用了第二种方式。以下是两种部署方式的参考链接:

2、代码逻辑开发

本方案采用前后端分离架构,前端使用基于Vue的Element UI框架,后端采用SpringBoot框架。

2.1、前端代码

关键步骤:

  1. 引入官方JS SDK:在前端页面中引入OnlyOffice提供的API JS文件。

    <div id="placeholder"></div>
    

    注意:需将 your-doc-server 替换为实际部署的OnlyOffice文档服务器地址。

  2. 初始化编辑器配置:按照官方API配置编辑器参数。

    const config = {
        document: {
            mode: 'edit',
            fileType: 'docx',
            key: String(Math.floor(Math.random() * 10000)), // 文档唯一标识
            title: route.query.name + '.docx',
            url: import.meta.env.VITE_APP_API_URL + `/getFile/${route.query.id}`, // 获取文档的后端接口
            permissions: {
                comment: true,
                download: true,
                modifyContentControl: true,
                modifyFilter: true,
                edit: true,
                fillForms: true,
                review: true,
            },
        },
        documentType: 'word',
        editorConfig: {
            user: { // 当前用户信息
                id: 'liu',
                name: 'liu',
            },
            customization: { // 自定义配置
                plugins: false,
                forcesave: true, // 启用强制保存
            },
            lang: 'zh',
            callbackUrl: import.meta.env.VITE_APP_API_URL + `/callback`, // 关键:回调接口地址
        },
        height: '100%',
        width: '100%',
    };
    new window.DocsAPI.DocEditor('onlyoffice', config);
    • url: 后端提供文档下载流的接口地址,例如 http://192.168.123.123:8089/getFile/12
    • callbackUrl: 核心回调接口,文档的任何操作(如保存)都会触发对该地址的请求。
2.2、后端代码

后端主要提供两个核心接口:/getFile/{id} 用于提供文档流,/callback 用于接收OnlyOffice服务器的回调通知(如保存文档)。

1. Maven依赖

<!-- httpclient -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpmime</artifactId>
</dependency>

2. 核心控制器 (OnlyOfficeController)

package com.ruoyi.web.controller.meetingminutes.onlyoffice;

import com.ruoyi.system.domain.MeetingTable;
import com.ruoyi.system.service.IMeetingTableService;
import com.ruoyi.web.controller.meetingminutes.utils.HttpsKitWithProxyAuth;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.util.Collections;

@RestController
public class OnlyOfficeController {

    @Autowired
    private IMeetingTableService meetingTableService;
    private String meetingMinutesFilePath; // 文档存储路径

    /**
     * 接口1:根据会议ID获取对应的Word文档流
     */
    @GetMapping("/getFile/{meeting_id}")
    public ResponseEntity<byte[]> getFile(HttpServletResponse response, @PathVariable Long meeting_id) throws IOException {
        MeetingTable meetingTable = meetingTableService.selectMeetingTableById(meeting_id);
        meetingMinutesFilePath = meetingTable.getMeetingMinutesFilePath();

        if (meetingMinutesFilePath == null || "".equals(meetingMinutesFilePath)) {
            return null; // 无文档时返回null
        }

        File file = new File(meetingMinutesFilePath);
        try (FileInputStream fileInputStream = new FileInputStream(file);
             BufferedInputStream fis = new BufferedInputStream(fileInputStream)) {

            byte[] buffer = new byte[fis.available()];
            fis.read(buffer);

            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.setContentDispositionFormData("attachment", URLEncoder.encode(file.getName(), "UTF-8"));
            return new ResponseEntity<>(buffer, headers, HttpStatus.OK);
        } catch (Exception e) {
            throw new RuntimeException("文件读取失败", e);
        }
    }

    /**
     * 接口2:处理OnlyOffice服务器的回调请求
     */
    @CrossOrigin(origins = "*")
    @PostMapping("/callback")
    public ResponseEntity<Object> handleCallback(@RequestBody CallbackData callbackData) {
        // 根据回调状态码处理不同逻辑
        Integer status = callbackData.getStatus();
        switch (status) {
            case 2: // 文档已准备好保存
            case 6: // 编辑中但当前状态已保存
                System.out.println("文档准备保存,开始下载...");
                String url = callbackData.getUrl(); // OnlyOffice提供的临时文件下载地址
                try {
                    saveFile(url); // 下载并覆盖原文件
                } catch (Exception e) {
                    System.out.println("保存文件异常");
                }
                System.out.println("保存成功.");
                break;
            case 3:
                System.out.println("文档保存出错");
                break;
            case 4:
                System.out.println("文档关闭,无更改");
                break;
            case 7:
                System.out.println("强制保存文档出错");
                break;
            default:
                break;
        }
        // 必须返回固定格式的响应
        return ResponseEntity.ok(Collections.singletonMap("error", 0));
    }

    /**
     * 从回调URL下载最新文档,覆盖本地文件
     */
    public void saveFile(String downloadUrl) throws URISyntaxException, IOException {
        HttpsKitWithProxyAuth.downloadFile(downloadUrl, meetingMinutesFilePath);
    }

    /**
     * 回调数据实体类
     */
    @Setter
    @Getter
    public static class CallbackData {
        private Integer status; // 状态码 (1:编辑中,2:待保存,3:保存错误,4:无更改关闭,6:编辑中已保存,7:强制保存错误)
        private String url;     // 最新文档下载地址 (status=2,3,6,7时有值)
        private String key;     // 文档唯一标识
        // ... 其他字段省略,可根据需要扩展
    }
}

3. 工具类 (HttpsKitWithProxyAuth) 该类是一个支持HTTP/HTTPS及代理的通用文件下载工具,核心方法 downloadFile 用于从回调的 url 下载文档并保存到指定路径。由于代码较长,此处仅说明其作用,具体实现可参考网络资源或根据项目HTTP客户端库(如RestTemplateOkHttp)进行简化。

3、问题与解决方案

3.1、服务访问失败

部署完成后,如果无法访问示例页面(example),请检查服务是否成功启动。

systemctl status ds*  # 查看所有OnlyOffice相关服务状态
3.2、文档加载或保存失败

此问题多与安全配置(Token验证、IP过滤)有关。需要修改OnlyOffice文档服务器的配置文件。

  1. 配置文件位置/etc/onlyoffice/documentserver/
  2. 修改 local.json:将 token 相关配置的 enable 设置为 false,以禁用Token验证。
    "token": {
      "enable": {
        "request": {
          "inbox": false,
          "outbox": false
        },
        "browser": false
      }
    }
  3. 修改 default.json
    • request-filtering-agent 下的 allowPrivateIPAddressallowMetaIPAddress 设为 true,允许私有IP访问。
    • 同样将 tokenenable 设置为 false
    • rejectUnauthorized (通常在 ssl 配置下) 设为 false(仅测试环境,生产环境请谨慎)。
  4. 修改配置后,重启OnlyOffice服务。
3.3、业务系统自身的鉴权拦截

如果您的SpringBoot应用本身通过Spring Security等组件进行了接口鉴权(如JWT Token),需要确保OnlyOffice回调接口(/callback)和文档获取接口(/getFile/*)能被匿名访问。 在Spring Security配置中,将这些路径加入白名单:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
        .antMatchers("/callback", "/getFile/**").permitAll() // 放行OnlyOffice相关接口
        .anyRequest().authenticated()
        ... // 其他配置
}
3.4、使用公网文档链接测试

在调试阶段,可以直接使用一个公网可访问的.docx链接来快速验证OnlyOffice服务是否部署成功。将前端配置中的 url 替换为测试链接即可:

url: 'https://d2nlctn12v279m.cloudfront.net/assets/docs/samples/zh/demo.docx',

4、总结

通过本文介绍的步骤,可以完成SpringBoot项目与OnlyOffice文档服务器的整合,实现前端在线编辑Word文档并自动保存回后端服务器的功能。整个过程涉及服务部署、前后端API对接和回调处理,特别是在Docker或Linux环境下部署OnlyOffice时,注意网络和权限配置是成功的关键。希望这篇实践指南能帮助你顺利实现需求。

您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区(YunPan.Plus) ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-3 14:20 , Processed in 0.094048 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 CloudStack.

快速回复 返回顶部 返回列表