docker 部署OnlyOffice实现在线编辑Word文档

有需求是实现前端页面可以对word文档进行编辑,并且可以进行保存,于是一顿搜索,找到开源第三方onlyoffice,实际上onlyOffice有很多功能,例如文档转化、多人协同编辑文档、文档打印等,我们只用到了文档编辑功能。

OnlyOffice的部署

部署分为docker部署方式和本地直接安装的方式,比较两种部署方式,docker是比较简单的一种,因为只要拉取相关镜像,然后启动时配置好对应的配置文件即可。

1、下载镜像

bash 复制代码
docker pull onlyoffice/documentserver
# x86架构
docker pull registry.cn-hangzhou.aliyuncs.com/qiluo-images/documentserver
# arm架构
docker pull registry.cn-hangzhou.aliyuncs.com/qiluo-images/linux_arm64_documentserver

2、#创建挂载目录

bash 复制代码
cd /data
mkdir onlyoffice
cd onlyoffice
mkdir logs data lib db

3、启动容器

bash 复制代码
docker run -i -t -d -p 9898:80 --name onlyoffice --restart=always -e TZ="Asia/Shanghai" \
-v /data/onlyoffice/logs:/var/log/onlyoffice \
-v /data/onlyoffice/data:/var/www/onlyoffice/Data \
-v /data/onlyoffice/lib:/var/lib/onlyoffice \
-v /data/onlyoffice/db:/var/lib/postgresql \
-e JWT_ENABLED=false  \
registry.cn-hangzhou.aliyuncs.com/qiluo-images/documentserver:latest

可选)如果需要开启jwt验证用户,则使用下面的命令

bash 复制代码
docker run -i -t -d -p 9898:80 --name onlyoffice --restart=always -e TZ="Asia/Shanghai" \
-v /data/onlyoffice/logs:/var/log/onlyoffice \
-v /data/onlyoffice/data:/var/www/onlyoffice/Data \
-v /data/onlyoffice/lib:/var/lib/onlyoffice \
-v /data/onlyoffice/db:/var/lib/postgresql \
-e JWT_ENABLED=true \
-e JWT_SECRET=wclflow \
-e JWT_HEADER=token \
registry.cn-hangzhou.aliyuncs.com/qiluo-images/documentserver:latest

如果开启jwtsecret,则通过以下命令,查看jwtsecret是否生效

bash 复制代码
docker exec onlyoffice /var/www/onlyoffice/documentserver/npm/json -f /etc/onlyoffice/documentserver/local.json 'services.CoAuthoring.secret.session.string'

4、检查是否安装成功,访问web页面

bash 复制代码
http://ip:9898/welcome/
bash 复制代码
问题定位:使用内部ip地址访问后端服务
解决方式:需要编辑配置文件允许私有ip通过
进入onlyoffice容器,然后编辑/etc/onlyoffice/documentserver/default.json​,
搜索并修改以下字段为true:
"request-filtering-agent" : {
	"allowPrivateIPAddress": true,
	"allowMetaIPAddress": true
},
最后退出容器,然后重启容器后就好了

OnlyOfficeController

bash 复制代码
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
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;

@Api(value = "OnlyOffice")
@RestController
publicclass OnlyOfficeController {
    
    @Autowired
    private IMeetingTableService meetingTableService;

    private String meetingMinutesFilePath;

    /**
     * 传入参数 会议id,得到会议纪要文件流,并进行打开
     */
    @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)) {
            returnnull;   // 当会议纪要文件为空的时候,就返回null
        }
        
        File file = new File(meetingMinutesFilePath);
        FileInputStream fileInputStream = null;
        InputStream fis = null;
        
        try {
            fileInputStream = new FileInputStream(file);
            fis = new BufferedInputStream(fileInputStream);
            byte[] buffer = newbyte[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"));
            
            returnnew ResponseEntity<>(buffer, headers, HttpStatus.OK);
        } catch (Exception e) {
            thrownew RuntimeException("e -> ", e);
        } finally {
            try {
                if (fis != null) fis.close();
            } catch (Exception e) {
                // ignore
            }
            try {
                if (fileInputStream != null) fileInputStream.close();
            } catch (Exception e) {
                // ignore
            }
        }
    }

    @CrossOrigin(origins = "*", methods = {RequestMethod.GET, RequestMethod.POST, RequestMethod.OPTIONS})
    @PostMapping("/callback")
    public ResponseEntity<Object> handleCallback(@RequestBody CallbackData callbackData) {
        
        // 状态监听
        // 参见 https://api.onlyoffice.com/editors/callback
        Integer status = callbackData.getStatus();
        
        switch (status) {
            case1: {
                // document is being edited  文档已经被编辑
                break;
            }
            case2: {
                // document is ready for saving, 文档已准备好保存
                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;
            }
            case3: {
                // document saving error has occurred, 保存出错
                System.out.println("document saving error has occurred, 保存出错");
                break;
            }
            case4: {
                // document is closed with no changes, 未保存退出
                System.out.println("document is closed with no changes, 未保存退出");
                break;
            }
            case6: {
                // document is being edited, but the current document state is saved, 编辑保存
                String url = callbackData.getUrl();
                try {
                    saveFile(url); // 保存文件
                } catch (Exception e) {
                    System.out.println("保存文件异常");
                }
                System.out.println("save success.");
            }
            case7: {
                // error has occurred while force saving the document. 强制保存文档出错
                System.out.println("error has occurred while force saving the document. 强制保存文档出错");
            }
            default: {
                // ignore
            }
        }
        
        // 返回响应
        return ResponseEntity.ok(Collections.singletonMap("error", 0));
    }

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

    @Setter
    @Getter
    publicstaticclass CallbackData {
        /** 用户与文档的交互状态 */
        Object changeshistory;
        Object history;
        String changesurl;
        String filetype;
        Integer forcesavetype;
        String key;
        /** 文档状态。1:编辑中;2:准备保存;3:保存出错;4:无变化;6:编辑保存;7:强制保存出错 */
        Integer status;
        String url;
        Object userdata;
        String[] users;
        String lastsave;
        String token;
    }
}

HttpsKitWithProxyAuth工具类

这是一个HTTP请求工具类,主要用于文件下载等操作(部分代码)

bash 复制代码
/**
 * 下载文件到本地
 * @param downloadUrl 下载地址
 * @param savePathAndName 保存路径和文件名
 */
public static void downloadFile(String downloadUrl, String savePathAndName) {
    HttpGet httpGet = new HttpGet(downloadUrl);
    httpGet.setHeader("User-Agent", USER_AGENT);
    httpGet.setConfig(requestConfig);

    CloseableHttpResponse response = null;
    InputStream in = null;

    try {
        response = getHttpClient().execute(httpGet, HttpClientContext.create());
        HttpEntity entity = response.getEntity();
        
        if (entity != null) {
            in = entity.getContent();
            FileOutputStream out = new FileOutputStream(new File(savePathAndName));
            IOUtils.copy(in, out);
            out.close();
        }
    } catch (IOException e) {
        logger.error("error", e);
    } finally {
        try {
            if (in != null) in.close();
        } catch (IOException e) {
            logger.error("error", e);
        }
        try {
            if (response != null) response.close();
        } catch (IOException e) {
            logger.error("error", e);
        }
    }
}

加载word文档失败

修改启动的配置文件,将token去除,配置文件位置:

bash 复制代码
/etc/onlyoffice/documentserver
修改 local.json
将参数token都改为false,去除token:
"token": {
   "enable": {
     "request": {
       "inbox": false,
       "outbox": false
     },
     "browser": false
   }
}

修改 default.json

将参数request-filtering-agent改为true,token也改为false,rejectUnauthorized改为false:

bash 复制代码
"request-filtering-agent": {
    "allowPrivateIPAddress": true,
    "allowMetaIPAddress": true
},
"token": {
    "enable": {
        "browser": false,
        "request": {
            "inbox": false,
            "outbox": false
        }
    }
},
"rejectUnauthorized": false

修改了以上配置参数后,重启服务,再次测试。

相关推荐
MyFreeIT2 小时前
Docker Manual
运维·docker·容器
修己xj3 小时前
Linux系统离线安装Docker完整指南
docker
周杰伦_Jay4 小时前
【 Kubernetes(K8s)完全指南】从入门到实战(含命令+配置+表格对比)
云原生·容器·kubernetes
__beginner__4 小时前
docker安装influxdb
运维·docker·容器
Empty_7776 小时前
K8S-daemonset控制器
云原生·容器·kubernetes
todoitbo7 小时前
openEuler 云原生实战:Docker Compose 部署 Nextcloud 企业级私有云
docker·云原生·容器·openeuler
一条懒鱼6667 小时前
K8S-daemonset控制器
云原生·容器·kubernetes
一周困⁸天.8 小时前
K8S-Service资源对象
云原生·容器·kubernetes
weixin_46689 小时前
Docker常用命令与操作
运维·docker·容器