基于springboot二次开发onlyoffice的Demo
最近研究了一下onlyoffice的开发一个在线协同编辑的小功能,在网上也找了很多博客,非常感谢一个老哥的知道,这里贴出他博客的地址:如何在 Windows 上 使用 ONLYOFFICE 协作编辑文档这个博客的作者已经开源了一套比较成熟的onlyoffice二次开发的系统,环境安装完毕后可直接运行使用。因为我这边的需求是要自己使用java后台集成一些所以,在开发过程中也请教了好多前边那个大神的很
最近研究了一下onlyoffice的开发一个在线协同编辑的小功能,在网上也找了很多博客,非常感谢一个老哥的知道,这里贴出他博客的地址:如何在 Windows 上 使用 ONLYOFFICE 协作编辑文档这个博客的作者已经开源了一套比较成熟的onlyoffice二次开发的系统,环境安装完毕后可直接运行使用。因为我这边的需求是要自己使用java后台集成一些所以,在开发过程中也请教了好多前边那个大神的很多问题,也都一一耐心的指导。好了话不多说了。
我的小Demo已经上传到码云:https://gitee.com/lei223/onlyofficeDemo,欢迎有需要的同学下载。里边包含了前后台的源码,数据库使用的sqlite 前端用的vue基础项目。欢迎下载。下面开始我的集成步骤了。安装过程看上述大神的教程即可。
1》Controller类
@Slf4j
@Controller
public class FileController {
@Value("${files.savePath}")
private String filePath;
@Value("${files.docservice.url.site}")
private String officeUrl;
@Value("${files.docservice.url.command}")
private String officeCommand;
@Autowired
private DocumentService documentService;
@Autowired
private FileUploadService uploadService;
@Autowired
RestTemplate restTemplate;
@ResponseBody
@PostMapping(value = "upload")
public ResponseEntity<Object> upload(@RequestParam("file") MultipartFile file, HttpServletRequest request) throws Exception {
if (file.isEmpty()) {
throw new Exception("上传文件不能为空");
}
FileUpload upload = new FileUpload();
String fileName = file.getOriginalFilename();
// if (!fileName.endsWith("xls") && !fileName.endsWith("xlsx")) {
// throw new Exception("请上传Excel文件");
// }
//更新保存文件信息到数据库
FileUtil.saveFile(file.getInputStream(), filePath + file.getOriginalFilename());
// System.out.println(fileName);
upload.setUpload_date(new Date());
System.out.println(".".indexOf(fileName));
System.out.println(fileName.length());
upload.setFile_type(fileName.substring(fileName.indexOf(".")));
upload.setFile_path(filePath);
upload.setFile_name(file.getOriginalFilename());
upload.setFile_size(file.getSize());
uploadService.save(upload);
//操作人
// String operator=request.getAttribute(StrUtil.USER_WORKNUMBER).toString();
// xxxService.saveUploadCkdExecl(file,operator);
return new ResponseEntity<Object>("上传成功", HttpStatus.OK);
}
/**
* \
* 查询所有上传文档信息接口
*
* @return
*/
@GetMapping("/filelist")
public ResponseEntity<Object> listFile() {
return new ResponseEntity<Object>(uploadService.list(), HttpStatus.OK);
}
// public ResponseEntity<Object> rview(){
//
// }
/**
* 下载文档接口
* @param name
* @param response
*/
@GetMapping("/download")
public void download(String name, HttpServletResponse response) {
try {
FileUtil.downLoadFile(name,filePath,response);
} catch (IOException e) {
e.printStackTrace();
}
}
@GetMapping("/edit")
public String editDocFile(@RequestParam String name,String userName,String userId,Model model) {
String path = filePath+name;
// String name = "cc.docx";
Document document = documentService.getDocument(documentService.buildDocument(path, name));
model.addAttribute("document", document);
// 如果该格式不支持编辑,则返回预览页面
if (!documentService.canEdit(document)) {
return "/demo";
}
model.addAttribute("documentEditParam", documentService.buildDocumentEditParam(userId, userName,name));
return "/editor";
}
/**
* 编辑文档时回调接口
* @param request
* @param response
* @throws IOException
*/
@RequestMapping("/callback")
public void saveDocumentFile(HttpServletRequest request, HttpServletResponse response) throws IOException {
//处理编辑回调逻辑
callBack(request, response);
}
/**
*
* @return
*/
@GetMapping("/editStatus")
public ResponseEntity<Object> getDoucmentEditStatus(String name) throws ParseException {
String url = officeUrl+officeCommand;
Map<String,String> map = new HashMap<String,String>();
map.put("c", "forcesave");
String docFileMd5 = Md5Utils.getFileMd5(new File(filePath+name));
if (StringUtils.isBlank(docFileMd5)) {
throw new DocumentException(ErrorCodeEnum.DOC_FILE_MD5_ERROR);
}
String pathShortMd5 = Md5Utils.md5(filePath + name);
String nameShortMd5 = Md5Utils.md5(name);
Hashids hashids = new Hashids(DocumentConstants.HASH_KEY);
// (将路径字符串短md5值 + 名称字符串短md5值) ==> 再转成短id形式 ==> 作为文档的key(暂且认为是不会重复的)
String key = hashids.encodeHex(String.format("%s%s%s", docFileMd5,pathShortMd5, nameShortMd5));
map.put("key", key);
map.put("userdata", "sample userdata");
JSONObject obj = (JSONObject) new JSONParser().parse(FileUtil.editStatus(url, JSON.toJSONString(map)));
return new ResponseEntity<Object>(obj, HttpStatus.OK);
}
/**
* 处理在线编辑文档的逻辑
* @param request
* @param response
* @throws IOException
*/
private void callBack(HttpServletRequest request, HttpServletResponse response) throws IOException {
PrintWriter writer = null;
JSONObject jsonObj = null;
System.out.println("===saveeditedfile------------");
try {
writer = response.getWriter();
Scanner scanner = new Scanner(request.getInputStream()).useDelimiter("\\A");
String body = scanner.hasNext() ? scanner.next() : "";
jsonObj = (JSONObject) new JSONParser().parse(body);
System.out.println(jsonObj);
System.out.println("===saveeditedfile:" + jsonObj.get("status"));
/*
0 - no document with the key identifier could be found,
1 - document is being edited,
2 - document is ready for saving,
3 - document saving error has occurred,
4 - document is closed with no changes,
6 - document is being edited, but the current document state is saved,
7 - error has occurred while force saving the document.
* */
if ((long) jsonObj.get("status") == 2) {
FileUtil.callBackSaveDocument(jsonObj,filePath,request, response);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
/*
* status = 1,我们给onlyoffice的服务返回{"error":"0"}的信息,这样onlyoffice会认为回调接口是没问题的,这样就可以在线编辑文档了,否则的话会弹出窗口说明
* 在线编辑还没有关闭,前端有人下载文档时,强制保存最新内容 当status 是6时说明有人在编辑时下载文档
* */
System.out.println(jsonObj.get("status"));
if ((long) jsonObj.get("status") == 6) {
//处理当文档正在编辑为关闭时,下载文档
if (((String)jsonObj.get("userdata")).equals("sample userdata")){
FileUtil.callBackSaveDocument(jsonObj,filePath,request, response);
}
System.out.println("====保存失败:");
writer.write("{\"error\":1}");
} else {
//执行删除编辑时下载保存的文件:
FileUtil.deleteTempFile(filePath,request.getParameter("fileName"));
writer.write("{\"error\":0}");
}
}
}
此类的主要接口有四个集成onlyoffice的在线协同编辑功能:
1》 /edit 此接口是返回给前端onlyoffice的编辑界面并打开要编辑的文件
2》 /download 下载文件接口,onlyoffice需要的接口之一,当将文档参数传入到onlyoffice服务器时需要一个download 的url 来让onlyoffice将文件下载到服务器配置参数如下:
3》/callback 接口 此接口是onlyoffice服务器对于你这服务的回调接口,当打开文档时,编辑完成后保存文档,或是正在编辑时,下载文档调用onlyoffice的强制保存命令之后都会回调这个接口。
4》 /editStatus 获取文档的编辑状态,用于编辑时下载文档,如果用户下载的文档正在被编辑,就可以先调用此接口,向onlyoffice服务器发送强制保存的命令,然后会调用callback接口进行保存。
以上的四个接口是,demo对于onlyoffice服务器的大部分的集成逻辑都在这几个接口的实现细节里。当然还有另外的,上传文件接口和提供文件列表查询的接口。
2。详细介绍一下协作编辑接口的实现逻辑/edit
1》 首先需要了解到如果想要打开一个编辑界面必须遵循onlyoffice文档中的前端文档要求
config = {
"document": {
"fileType": "docx",
"key": "Khirz6zTPdfd7",
"title": "Example Document Title.docx",
"url": "https://example.com/url-to-example-document.docx"
},
"documentType": "word",
"editorConfig": {
"callbackUrl": "https://example.com/url-to-callback.ashx"
}
};
var docEditor = new DocsAPI.DocEditor("placeholder", config);
上述的config 对象就是 打开文本编辑器最基本的配置。/edit 接口的内容就是构建了上述config配置的过程。其中key参数,是一个文档能否多人协同编辑的一个关键点,也就是说多人同事操作的同一个文档时,此时文档的key值肯定是同一个,demo使用算法保持同一个文件key值的唯一。 document下的url 参数就是下载接口的路径 在demo中的接口地址是 /download config下的editorConfig 是相关回调的配置,其中callbackUrl 代表编辑完成以后 文档将要保存,并将最后结果返回给onlyOffice服务器,onlyoffice服务器需要我们的服务器给它响应为 {"error":0}的响应结果来通知服务器此文档没有问题,否则刚开始无法正常编辑文档。
2》 /edit 的就是做了上述相关配置的构建。具体的实现细节看代码便知。
3. /download接口的实现:
上图是下载接口的实现逻辑。
4. /callback实现逻辑:
@RequestMapping("/callback") public void saveDocumentFile(HttpServletRequest request, HttpServletResponse response) throws IOException { PrintWriter writer = null; JSONObject jsonObj = null; System.out.println("===saveeditedfile------------"); try { writer = response.getWriter(); Scanner scanner = new Scanner(request.getInputStream()).useDelimiter("\\A"); String body = scanner.hasNext() ? scanner.next() : ""; jsonObj = (JSONObject) new JSONParser().parse(body); System.out.println(jsonObj); System.out.println("===saveeditedfile:" + jsonObj.get("status")); /* 0 - no document with the key identifier could be found, 1 - document is being edited, 2 - document is ready for saving, 3 - document saving error has occurred, 4 - document is closed with no changes, 6 - document is being edited, but the current document state is saved, 7 - error has occurred while force saving the document. * */ if ((long) jsonObj.get("status") == 2) { FileUtil.callBackSaveDocument(jsonObj,filePath,request, response); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } /* * status = 1,我们给onlyoffice的服务返回{"error":"0"}的信息,这样onlyoffice会认为回调接口是没问题的,这样就可以在线编辑文档了,否则的话会弹出窗口说明 * 在线编辑还没有关闭,前端有人下载文档时,强制保存最新内容 当status 是6时说明有人在编辑时下载文档 * */ System.out.println(jsonObj.get("status")); if ((long) jsonObj.get("status") == 6) { //处理当文档正在编辑为关闭时,下载文档 if (((String)jsonObj.get("userdata")).equals("sample userdata")){ FileUtil.callBackSaveDocument(jsonObj,filePath,request, response); } System.out.println("====保存失败:"); writer.write("{\"error\":1}"); } else { //执行删除编辑时下载保存的文件: FileUtil.deleteTempFile(filePath,request.getParameter("fileName")); writer.write("{\"error\":0}"); } }
因为有点长一张截图放不下,此代码是当编辑回调后保存文件。此代码是摘自onlyoffice官方文档的中javasample中的代码.官方地址是sample 加入了自己保存逻辑。实现在编辑时,有人下载也能保存文档。保存文档操作的逻辑如下:
/** * 编辑以后保存文件 * @param jsonObj * @param filePath * @param request * @param response * @throws IOException */ public static void callBackSaveDocument(JSONObject jsonObj, String filePath, HttpServletRequest request, HttpServletResponse response) throws IOException { /* * 当我们关闭编辑窗口后,十秒钟左右onlyoffice会将它存储的我们的编辑后的文件,,此时status = 2,通过request发给我们,我们需要做的就是接收到文件然后回写该文件。 * */ /* * 定义要与文档存储服务保存的编辑文档的链接。当状态值仅等于2或3时,存在链路。 * */ String downloadUri = (String) jsonObj.get("url"); System.out.println("====文档编辑完成,现在开始保存编辑后的文档,其下载地址为:" + downloadUri); //解析得出文件名 //String fileName = downloadUri.substring(downloadUri.lastIndexOf('/')+1); String fileName = request.getParameter("fileName"); System.out.println("====下载的文件名:" + fileName); URL url = new URL(downloadUri); java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection(); InputStream stream = connection.getInputStream(); File savedFile = new File(filePath + fileName); //判断是否包含userdata字段信息,如果有证明前端在编辑文档没有关闭时下载正在在线编辑的文档, // 此时将文件保存为带一个前缀 v1的文档供前端下载。 如 v1xxx.docx if (null!=((String) jsonObj.get("userdata"))&&((String) jsonObj.get("userdata")).equals("sample userdata")) { savedFile = new File(filePath + "v1" + fileName); } try (FileOutputStream out = new FileOutputStream(savedFile)) { int read; final byte[] bytes = new byte[1024]; while ((read = stream.read(bytes)) != -1) { out.write(bytes, 0, read); } out.flush(); } connection.disconnect(); }
5. /editStatus 接口的实现
FileUtil.editStatus方法具体实现
/** * 发送网路请求查看是否正在编辑 * @param path * @param params * @return */ public static String editStatus(String path, String params) { OutputStreamWriter out = null; BufferedReader in = null; StringBuilder result = new StringBuilder(); HttpURLConnection conn = null; try { URL url = new URL(path); conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); //发送POST请求必须设置为true conn.setDoOutput(true); conn.setDoInput(true); //设置连接超时时间和读取超时时间 conn.setConnectTimeout(30000); conn.setReadTimeout(10000); conn.setRequestProperty("Content-Type", "application/json"); conn.setRequestProperty("Accept", "application/json"); //获取输出流 out = new OutputStreamWriter(conn.getOutputStream()); // String jsonStr = "{\"c\":\"forcesave\", \"key\":\"WpP7m85eNQSEOoepp31oIYVG2oJyJJcvkLdoywgvs1k3ywm3Omuxk4\",\"userdata\":\"sample userdata\"}"; out.write(params); out.flush(); out.close(); //取得输入流,并使用Reader读取 if (200 == conn.getResponseCode()) { return IOUtils.toString(conn.getInputStream()); } else { System.out.println("ResponseCode is an error code:" + conn.getResponseCode()); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (out != null) { out.close(); } if (in != null) { in.close(); } } catch (IOException ioe) { ioe.printStackTrace(); } } return ""; }
发送完请求以后,onlyoffice就会调用回调接口 进行文件强制保存就会触发callback接口中的这一步逻辑:
此demo具体实现逻辑就是上述。
下边说下修改配置 然后运行起来。
先说前端配置。将请求url中的ip加端口都替换成你本地服务的ip+端口
修改 java工程中的url配置:url的配置全部在application.yml中:
如果确保上树的修改都已经做了,就可以运行前后台的项目了
更多推荐
所有评论(0)