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

175

积分

0

好友

23

主题
发表于 前天 01:16 | 查看: 6| 回复: 0

在项目开发中,需要实现前端页面在线编辑Word文档并保存的功能。经过调研,选择了开源组件OnlyOffice,它支持文档转换、多人协同编辑、文档打印等丰富功能,本文重点介绍文档编辑相关的实现方案。

1、OnlyOffice部署

OnlyOffice支持Docker部署和本地安装两种方式。Docker部署相对简单,只需拉取镜像并配置参数即可启动;本地安装步骤稍多但稳定性较好。以下是两种部署方式的参考文档:

本地部署经过验证可完整实现功能,建议初次使用者参考Ubuntu部署方案。

2、代码开发实现

采用Vue + Element UI作为前端框架,SpringBoot作为后端技术栈进行开发。

2.1 前端配置

参考OnlyOffice官方API文档:

在页面中引入OnlyOffice JS文件:

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

注意将documentserver替换为实际的OnlyOffice服务地址。

核心配置代码:

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)

其中import.meta.env.VITE_APP_API_URL需配置为实际的后端服务地址,callbackUrl用于处理文档操作的回调通知。

2.2 后端实现

添加HTTP客户端依赖:

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

核心控制器代码:

@Api(value = "OnlyOfficeController")
@RestController
public class OnlyOfficeController {
    @Autowired
    private IMeetingTableService meetingTableService;

    private String meetingMinutesFilePath;

    @ApiOperation(value = "OnlyOffice")
    @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;
        }

        File file = new File(meetingMinutesFilePath);
        FileInputStream fileInputStream = null;
        InputStream fis = null;

        try {
            fileInputStream = new FileInputStream(file);
            fis = new BufferedInputStream(fileInputStream);
            byte[] buffer = new byte[fis.available()];
            fis.read(buffer);
            fis.close();

            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 -> ", e);
        } finally {
            try {
                if (fis != null) fis.close();
            } catch (Exception e) {}
            try {
                if (fileInputStream != null) fileInputStream.close();
            } catch (Exception e) {}
        }
    }

    @CrossOrigin(origins = "*", methods = {RequestMethod.GET, RequestMethod.POST, RequestMethod.OPTIONS})
    @PostMapping("/callback")
    public ResponseEntity<Object> handleCallback(@RequestBody CallbackData callbackData) {
        Integer status = callbackData.getStatus();

        switch (status) {
            case 1:
                // 文档正在编辑
                break;
            case 2:
                // 文档准备保存
                System.out.println("document is ready for saving");
                String url = callbackData.getUrl();
                try {
                    saveFile(url);
                } catch (Exception e) {
                    System.out.println("保存文件异常");
                }
                System.out.println("save success.");
                break;
            case 3:
                // 文档保存错误
                System.out.println("document saving error has occurred,保存出错");
                break;
            case 4:
                // 文档关闭无修改
                System.out.println("document is closed with no changes,未保存退出");
                break;
            case 6:
                // 编辑中自动保存
                String autoSaveUrl = callbackData.getUrl();
                try {
                    saveFile(autoSaveUrl);
                } catch (Exception e) {
                    System.out.println("保存文件异常");
                }
                System.out.println("save success.");
                break;
            case 7:
                // 强制保存错误
                System.out.println("error has occurred while force saving the document. 强制保存文档出错");
                break;
            default:
                break;
        }

        return ResponseEntity.<Object>ok(Collections.singletonMap("error", 0));
    }

    public void saveFile(String downloadUrl) throws URISyntaxException, IOException {
        HttpsKitWithProxyAuth.downloadFile(downloadUrl, meetingMinutesFilePath);
    }

    @Setter
    @Getter
    public static class CallbackData {
        private Object changeshistory;
        private Object history;
        private String changesurl;
        private String filetype;
        private Integer forcesavetype;
        private String key;
        private Integer status;
        private String url;
        private Object userdata;
        private String[] users;
        private String lastsave;
        private String token;
    }
}

工具类HttpsKitWithProxyAuthJsonUtil提供文件下载和JSON处理功能,完整代码参见文末参考链接。

3、常见问题处理

3.1 服务启动异常

部署完成后若example访问失败,使用以下命令检查服务状态:

systemctl status ds*

3.2 文档加载失败

修改OnlyOffice配置文件解决权限问题,配置文件位置:/etc/onlyoffice/documentserver

  • local.json中禁用token验证:

    "token": {
    "enable": {
        "request": {
            "inbox": false,
            "outbox": false
        },
        "browser": false
    }
    }
  • default.json中调整安全设置:

    "request-filtering-agent": {
    "allowPrivateIPAddress": true,
    "allowMetaIPAddress": true
    },
    "token": {
    "enable": {
        "browser": false,
        "request": {
            "inbox": false,
            "outbox": false
        }
    }
    },
    "rejectUnauthorized": false

修改后重启服务生效。

3.3 系统Token验证冲突

若后端系统需要Token验证,在安全配置中放行相关接口:

requests.antMatchers("/callback", "/getFile/*", "/login", "/register", "/captchaImage").permitAll()

3.4 文档地址访问优化

除API方式外,可通过Nginx代理直接访问文档地址。测试时可使用在线文档链接:

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

通过以上配置和问题处理,可稳定实现基于Docker的OnlyOffice在线文档编辑系统。

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

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

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

Powered by Discuz! X3.5

© 2025-2025 CloudStack.

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