永中Office官网,相比于PageOffice,个人觉得从使用方面来说,永中Office好用一点,永中Office对于开发者来说,有两个选择,一个是在线版webOffice功能较少(对于我这种需求对文档内容细节把控的来说,不考虑了),另一个是NP插件版,这个版本是和永中技术支持的小姐姐对接的,也是我推荐使用的版本
从使用层面来说,永中NP插件只需要两个东西,一个是NP插件的js文件需要引入到项目中,其次是客户端需要安装永中Office软件,因为NP插件实际上是调用本地的永中Office软件,而且永中的授权模式是客户端软件授权,也就是说,每个客户端都需要安装,并且授权才能使用,这点和PageOffice不同,PageOffice是服务端授权
永中NP插件只能使用在360浏览器,因为我的项目需要支持在Linux系统上使用
NP插件是在浏览器上新开一个tab,并显示内容,个人觉得最好用的方式
接下来继续分析项目需求,首先看这个插件能否满足,我的项目中使用的所有功能都能满足
新建文件新建模板文件新建模板文件(带标签替换)编辑文件查询多个文章段落合并为一个文件(数据库多个段落存的是字符串)保存时设置其他参数值多个二进制文件合并未一个文件(多个文件合并)阅读文件(只读模式)下载文件通过代码构建整篇文章上面的出了最后一点难度大,其他的都是常规操作,通过代码构建整篇文章意思是用代码拼接出一篇800字作文,这样好理解,包括文字段落、换行、加粗、样式、首航缩进等,一篇标准的文件,我的项目实际上是生成1:1的政府红头文件,对文字的要求更是严格
前面说过,NP插件只有一个js文件
在网页头部增加js文件就可以了
<%-- 永中office --%> <script type="text/javascript" src="<%=path%>/static/js/yozoOffice/yozoOffice.js"></script>相比PageOffice,永中NP插件简洁的多,PageOffice的代码都是写在java的,永中NP的代码都是写在js中的
下面以新建一个模板文件为例,然后保存到服务器,首先前端请求后台,新建文件
前端发起请求
var param = '?title=' + encodeURI(obj.field.title) + '&issueNumber=' + obj.field.issueNumber + '&userIds=' + userIds.join(',') + '&inviteDate=' + encodeURI(obj.field.inviteDate) + '&attachment=' + encodeURI(obj.field.attachment) + '&attachmentName=' + encodeURI(obj.field.attachmentName) + '&delayTimeLimit=' + encodeURI(obj.field.delayTimeLimit) + '&missTimeLimit=' + encodeURI(obj.field.missTimeLimit) + '&orgId=${sessionScope.userInfoSession.orgId}' + '&createUser=${sessionScope.userInfoSession.id}'; window.open('<%=path%>/test/addInitStep.action' + param);后台一样的返回一个页面
/** * 添加Word页面 * * @param entity * @param model * @return */ @GetMapping("addInitStep") public String addInitStep(InvitedPaperVo entity, Model model) { UserInfoSession ui = (UserInfoSession) SessionUtils.getSessionAttribute(SessionConst.PC_USER_SESSION); UserInfoVo userInfoVo = userInfoService.getUserInfoById(ui); Map<String, Object> map = new HashMap<>(16); map.put("inviteYear", entity.getInviteDate().substring(0, 5)); map.put("inviteDate", DateUtils.dateClearZero(entity.getInviteDate())); map.put("issueNumber", String.valueOf(entity.getIssueNumber())); map.put("content", ""); map.put("phone", userInfoVo.getOfficeTel() != null ? userInfoVo.getOfficeTel() : ""); map.put("createUserName", userInfoVo.getName() != null ? userInfoVo.getName() : ""); model.addAttribute("bookmarkJson", JSON.toJSON(map)); return "admin/invited_paper_add_office"; }编写返回的页面,用来承载插件显示的内容
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%String path = request.getContextPath();%> <!DOCTYPE html> <html> <head> <title>新建模板文件</title> <%@ include file="./common/head.jsp" %> </head> <body> <div style="width: 230px; float: left; padding: 10px;"> <button type="button" class="layui-btn layui-btn-blue" lay-submit lay-filter="submitBtn">保存文档</button> </div> <div style="width: calc(100% - 250px); float: left;"> <div id="yozo"></div> </div> <%@ include file="./common/js.jsp" %> <script type="text/javascript"> /** --------------------------------------- 初始化永中 start */ // 初始化插件 var Application; Application = init("yozo", "100%", $(window).height()); Application.ActionEnabledWithYozoID(4, false); Application.openDocumentRemote(serverBaseUrl + "/office-templates/invited_paper.doc", false); // 处理标签替换 var bookmarkJson = JSON.parse('${bookmarkJson}'); var bookmarks = Application.ActiveDocument.Bookmarks; replaceBookmarks(bookmarkJson, bookmarks); /** --------------------------------------- 初始化永中 end */ layui.config({ base: '<%=path%>/static/layuiadmin/' }).extend({ popup: 'modules/popup' }).use(['layer', 'form'], function () { var form = layui.form; // 提交数据 form.on('submit(submitBtn)', function (obj) { layer.confirm('保存文档', {icon: 0, title: '提示'}, function () { var param = '?' + getParamByJson(getQueryString()); if (Application.saveURL(serverBaseUrl + '/invitedPaper/save.action' + param)) { alert('保存成功'); history.go(-1); window.close(); } else { alert('保存失败:请检查网络连接') } }); return false; }); }); </script> </body> </html>保存到服务器,这里使用了一个工具类,我是自己编写的工具类
/** * 保存基本信息 * * @param invitedPaperVo * @return */ @RequestMapping("save") @ResponseBody public void save(InvitedPaperVo invitedPaperVo, HttpServletRequest request) { invitedPaperVo.setContent(YoZoUtils.getFileByte(request)); invitedPaperService.saveInvitedPaper(invitedPaperVo); }YoZoUtils.getFileByte(request)工具类的内容是,从当前request中读取到文件,并转为二进制,我的服务器是保存的二进制文件
import javax.servlet.http.HttpServletRequest; import java.io.*; /** * 永中插件 工具类 * * @author Tellsea * @date 2020/09/22 */ public class YoZoUtils { /** * 将前端请求的文件,转为byte[]类型,兼容老插件PageOffice * * @param request * @return */ public static byte[] getFileByte(HttpServletRequest request) { try { if (request.getContentLength() > 0) { String localFileName = System.currentTimeMillis() + ".doc"; String realFileName = request.getSession().getServletContext().getRealPath("/office-templates/temp/") + localFileName; InputStream in = request.getInputStream(); File file = new File(realFileName); if (!file.getParentFile().exists()) { try { file.getParentFile().mkdirs(); file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } FileOutputStream o = new FileOutputStream(file); byte b[] = new byte[1024]; int n; while ((n = in.read(b)) != -1) { o.write(b, 0, n); } o.close(); in.close(); byte[] bytes = fileConvertToByteArray(file); deleteFile(realFileName); return bytes; } else { System.out.println("保存的文件不能为空"); } } catch (IOException e) { System.out.println("处理文件错误"); e.printStackTrace(); } return null; } /** * 把一个文件转化为byte字节数组。 * * @return */ public static byte[] fileConvertToByteArray(File file) { byte[] data = null; try { FileInputStream fis = new FileInputStream(file); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int len; byte[] buffer = new byte[1024]; while ((len = fis.read(buffer)) != -1) { baos.write(buffer, 0, len); } data = baos.toByteArray(); fis.close(); baos.close(); } catch (Exception e) { e.printStackTrace(); } return data; } /** * 删除单个文件 * * @param fileName 要删除的文件的文件名 * @return 单个文件删除成功返回true,否则返回false */ public static boolean deleteFile(String fileName) { File file = new File(fileName); // 如果文件路径所对应的文件存在,并且是一个文件,则直接删除 if (file.exists() && file.isFile()) { if (file.delete()) { return true; } else { return false; } } else { return false; } } /** * 强制删除 * * @param fileName * @return */ public static boolean forceDelete(String fileName) { File f = new File(fileName); boolean result = false; int tryCount = 0; while (!result && tryCount++ < 10) { System.gc(); result = f.delete(); } return result; } }上面是保存二进制的方法,建议保存文件到服务器,然后将访问路径保存到数据库,具体的可以和技术客服详细对接