文章最后提供源码下载地址
市面上处理文字的的办公软件有很多,包括WPS、MSOffice、永中OFFICE,当然还有开源的openoffice、liboffice等。我们在项目开发过程中经常会遇到预览WORD文件,数据库中数据自动填充word模板等需求。现在能够满足以上需求的技术有很多,服务端可通过POI\aspose等处理,也可通过客户端调用OFFICE组件处理,本人曾经在这方便做了很多测试,最终发现兼容性最好的、接口对JAVA程序员最友好的就属永中OFFICE,因为它基本就是JAVA实现的,使用起来非常方便。
我的测试环境使用的是永中2016版本,它运行要求JRE1.6,且我发现它应该是对JRE进行过重构,按永中SDK要求编写代码通过自ORACAL官网下载的jdk1.6编译后运行是失败的,现在都2021年了,我们的项目绝大多数都JDK1.8以上版本了,那么怎么让SDK兼容我们的项目呢?怎么实现标题中提到的两个需求呢?下面我说说我的处理方法吧:
1、下载永中软件并安装(官网下载即可)
2、安装后打开安装路径可以看到如下图
永中软件安装目录
JRE:即永中软件的运行环境
Yozo_Office.jar: 即永中为开发者提供的SDK,可以将jar导入到工程中
3、编写WORD文件处理服务组件
处理word文件的代码片段,详细代码请在文后下载源码查阅
/**
* 将word文件转换为对应格式的文件的字节数组
* @param type 将word文件转换成的文件格式 pdf、html\ofd\txt\xml
* @return
* @throws IOException
*/
public byte[] convertFile(String type) throws IOException {
int typePdf = FileConstants.TYPE_PDF;
if("html".equals(type.toLowerCase())) {//此功能转换后乱码,后期可采用 this.Workbook.saveAs("D:/2.html"); 方式存储html后,将字节返回
typePdf= FileConstants.FILETYPE_HTML;
}else if("ofd".equals(type.toLowerCase())) {
typePdf= FileConstants.TYPE_OFD; // 这个是不成功的,应该是版本太低
}else if("txt".equals(type.toLowerCase())) {
typePdf = FileConstants.TYPE_TXT;
}else if("xml".equals(type.toLowerCase())) {
typePdf = FileConstants.FILETYPE_XML;
}else if("doc".equals(type.toLowerCase())||"xls".equals(type.toLowerCase())||"ppt".equals(type.toLowerCase())) {
typePdf = FileConstants.TYPE_MS;
}else if("docx".equals(type.toLowerCase())||"xlsx".equals(type.toLowerCase())||"pptx".equals(type.toLowerCase())) {
typePdf = FileConstants.TYPE_MS_EX;
}
return this.workbooks.getWorkbookAsByteArray(workbook, typePdf);
}
/**
* 替换word模板中的书签
* @param jsonObject 数据内容 {“bookmarkname”:”test“}
*/
public void replaceBookMark(JSONObject jsonObject) {
BookMarks bookMarks = this.document.getBookMarks();
BookMark[] allBookmarks = bookMarks.getAllBookmarks();
for(BookMark bookMark:allBookmarks){
String name = bookMark.getName();
TextRange range = bookMark.getRange();
//if(name!=null)name=name.replace("PO_","");
String value = "";
Object o = jsonObject.get(name);
if(o!=null){
value=jsonObject.get(name).toString();
}
try {
range.insertText(value);
}catch (Exception e){
range.insertText(value);
}
}
}
/**
* 导出数据成excel文件
* @param jsonObject 数据内容 {“bookmarkname”:”test“}
*/
public byte[] exportData2File(JSONArray taskArray,int allrow) {
}
4、(重点)解决word文件处理组件与我们的项目文件交互问题
本人通过SOCKET即时通讯服务解决数据交互问题
/**
* 文件传输Server端<br>
* 功能说明:
* @Author 空中智囊
* @Date 2016年09月01日
* @version 1.0
*/
public class socketService extends ServerSocket {
private static final int SERVER_PORT = 8899; // 服务端端口
private WordUtil wordUtil=null;
public SocketService() throws Exception {
super(SERVER_PORT);
this.wordUtil=new WordUtil();
}
/**
* 使用线程处理每个客户端传输的文件
* @throws Exception
*/
public void load() throws Exception {
System.out.println("服务端启动,监听端口为:" SERVER_PORT);
while (true) {
// server尝试接收其他Socket的连接请求,server的accept方法是阻塞式的
Socket socket = this.accept();
socket.setSoTimeout(1200000);
/**
* 我们的服务端处理客户端的连接请求是同步进行的, 每次接收到来自客户端的连接请求后,
* 都要先跟当前的客户端通信完之后才能再处理下一个连接请求。 这在并发比较多的情况下会严重影响程序的性能,
* 为此,我们可以把它改为如下这种异步处理与客户端通信的方式
*/
// 每接收到一个Socket就建立一个新的线程来处理它
new Thread(new Task(socket,wordUtil)).start();
}
}
/**
* 入口
* @param args
*/
public static void main(String[] args) {
try {
SocketService server = new SocketService(); // 启动服务端
server.load();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 处理客户端传输过来的文件线程类
*/
public class Task implements Runnable {
@Override
public void run() {
System.out.println("===客户端连接成功=====");
System.out.println("****************************************************************");
SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 转换要求的格式
*/
try {
/********************************读取文件信息********************************/
dis = new DataInputStream(socket.getInputStream());
// 文件名和长度
String fileName = dis.readUTF();//1、文件名字
long fileLength = dis.readLong();//2、长度
String toext = dis.readUTF();//3、扩展名
String taskType=dis.readUTF();//4、文件操作类型
System.out.println("针对文件的操作类型=====" taskType);
String valueObject=dis.readUTF();//5、替换书签的值
System.out.println(format.format(new Date()) ":开始接收文件");
ByteArrayOutputStream bos = new ByteArrayOutputStream((int)fileLength);
byte[] bytes = new byte[1024];
int length = 0;
while((length = dis.read(bytes, 0, bytes.length)) != -1) {
bos.write(bytes, 0, length);
}
byte[] filebytes = bos.toByteArray();
System.out.println("原始文件大小=====" fileLength ",实际接收文件大小=" filebytes.length);
/********************************读取文件信息结束********************************/
dos = new DataOutputStream(socket.getOutputStream());
/********************************校验文件信息********************************/
boolean process=true;
if(fileLength>0){
}else{
dos.writeUTF("error");
dos.flush();
dos.writeUTF("文件没有任何内容,请重新传送");
dos.flush();
process=false;
}
if(filebytes.length!=fileLength){
dos.writeUTF("error");
dos.flush();
dos.writeUTF("接受文件与实际文件大小不符合,请重新传送文件");
dos.flush();
process=false;
}
/********************************校验文件信息结束********************************/
/********************************处理文件********************************/
if(process){
byte[] fileBytes=null;
this.wordUtil.openFile(filebytes,fileName);//打开院文件
//workbook =workbooks.createWorkbookFromByteArray(filebytes,fileName);
String lowerExt = toext.toLowerCase();
if("convertFile".equals(taskType)){
System.out.println("开始将文件[" fileName "]转换成====" lowerExt);
fileBytes=this.wordUtil.convertFile(lowerExt);
System.out.println(format.format(new Date()) ":转换" toext "完成");
}else if("replaceBookMark".equals(taskType)){
System.out.println("开始将文件[" fileName "]书签进行替换====");
JSONObject jsonObject = JSONObject.fromObject(valueObject);
this.wordUtil.replaceBookMark(jsonObject);
fileBytes = this.wordUtil.convertFile(lowerExt);
System.out.println("===============替换书签完成============");
}else if("exportTask".equals(taskType)) {//处理业务数据 导出任务数据
System.out.println("开始导出业务数据====" valueObject);
ServiceUtil serviceUtil = new ServiceUtil(this.wordUtil);
JSONObject jsonObject = JSONObject.fromObject(valueObject);
fileBytes = serviceUtil.exportData2File(jsonObject.getJSONArray("datalist"), jsonObject.getInt("size"));
System.out.println("===============导出业务数据完成============");
}
/********************************处理文件结束********************************/
if(fileBytes==null){
dos.writeUTF("error");
dos.flush();
dos.writeUTF("处理文件过程中错误");
dos.flush();
process=false;
}
/********************************返回处理过的文件********************************/
if(process){
dos.writeUTF("info");//文件处理完成,将信息返回到客户端
dos.flush();
int fileBytelength = fileBytes.length;//转换后的文件长度
System.out.println(format.format(new Date()) ":======== 服务端开始发送文件流,文件大小(" getFormatFileSize(fileBytelength) ") ========");
dos.writeLong(fileBytelength);
dos.flush();
dos.write(fileBytes, 0, fileBytelength);//将文件一起写入到输出流发送
dos.flush();
System.out.println(format.format(new Date()) ":======== 发送文件流成功 ========");
}
/********************************返回处理过的文件完成********************************/
}
} catch (Exception e) {
String error = e.toString();
System.out.println("error===================" error);
StackTraceElement[] stackTrace = e.getStackTrace();
for(StackTraceElement s:stackTrace){
int lineNumber = s.getLineNumber();
String methodName = s.getMethodName();
String className = s.getClassName();
String filename = s.getFileName();
System.out.print("err:" filename " " className " " methodName " " lineNumber);
System.out.println("");
}
try {
dos.writeUTF("error");
dos.flush();
dos.writeUTF("处理文件过程中错误==" e.toString());
dos.flush();
}catch (Exception ex){
String exrror =ex.toString();
System.out.println("返回数据处理错误信息===================" exrror);
}
}finally {
System.out.println("关闭资源");
try {
if(wordUtil!=null)wordUtil.close();
socket.close();
} catch (Exception e) {
String error = e.toString();
System.out.println(error);
e.printStackTrace();
}
System.out.println("****************************************************************");
}
}
/**
* 文件传输Clinet端<br>
* 功能说明:
* @Author 空中智囊
* @Date 2016年09月01日
* @version 1.0
*/
public class SocketClient extends Socket {
public static final Logger LOGGER = LoggerFactory.getLogger(SocketClient.class);
private static final String SERVER_IP = "127.0.0.1"; // word文件组件处理服务IP地址
private static final int SERVER_PORT = 8899; // word文件组件处理服务端口
private int soTimeout = 60000; // 服务链接超时时间 60s
private Socket client = this;
private FileInputStream fis;
private DataOutputStream dos;
private DataInputStream dis;
private FileOutputStream fos;
public SocketClient(String listenip, int listenport) throws Exception {
super(listenip, listenport);
this.setSoTimeout(this.soTimeout);
LOGGER.info("Cliect[port:" this.client.getLocalPort() "] 成功连接服务端");
}
public SocketClient() throws Exception {
super(SERVER_IP, SERVER_PORT);
this.setSoTimeout(this.soTimeout);
LOGGER.info("Cliect[port:" this.client.getLocalPort() "] 成功连接服务端");
}
public SocketClient(String listenip, int listenport, int soTimeout) throws Exception {
super(listenip, listenport);
this.setSoTimeout(soTimeout);
LOGGER.info("Cliect[port:" this.client.getLocalPort() "] 成功连接服务端");
}
/**
* 处理word文件
* @param srcRealPath 模板word文件路径绝对地址
* @param descRealPath 处理后的文件存放地址绝对路径
* @param taskType 处理文件的类型 convertFile/replaceBookMark/exportTask
* @param jsonObject 传给服务端的数据对象,这个参数可根据服务端需求进行调整
* @return 处理结果
*/
public JSONObject processOffice(String srcRealPath, String descRealPath, String taskType, JSONObject jsonObject) {
JSONObject rtnObject = new JSONObject();
String code = "200";
String message = "";
try {
File file = new File(srcRealPath);
if (!file.exists() || !file.canWrite()) {
code = "200";
message = "文件不存在,或已被占用";
rtnObject.element("code", code);
rtnObject.element("message", message);
JSONObject var41 = rtnObject;
return var41;
}
LOGGER.info(srcRealPath "===>" descRealPath);
if (file.exists() && file.canWrite()) {
String filename = file.getName();
this.fis = new FileInputStream(file);
this.dos = new DataOutputStream(this.client.getOutputStream());
this.dos.writeUTF(filename);//文件名字
this.dos.flush();
this.dos.writeLong(file.length());//文件长度
this.dos.flush();
String ext = descRealPath.substring(descRealPath.lastIndexOf(".") 1, descRealPath.length());
this.dos.writeUTF(ext);//源文件后缀名字
this.dos.flush();
this.dos.writeUTF(taskType);//任务类型
this.dos.flush();
if (YOZOOfficeUtil.PROCESS_TYPE_CONVERTFILE.equals(taskType)) {
this.dos.writeUTF(jsonObject.toString());
this.dos.flush();
}
LOGGER.info("======== 开始向服务端传送源文件" srcRealPath " ========");
byte[] bytes = new byte[1024];
long progress = 0L;
int length;
while((length = this.fis.read(bytes, 0, bytes.length)) != -1) {
this.dos.write(bytes, 0, length);
this.dos.flush();
progress = (long)length;
LOGGER.info("| " 100L * progress / file.length() "% |");
}
LOGGER.info("======== 文件传输成功 (" file.length() / 1048576L ")M========");
this.client.shutdownOutput();
LOGGER.info("======== 开始转换" ext " ========");
InputStream inputStream = this.client.getInputStream();
this.dis = new DataInputStream(inputStream);
String result = this.dis.readUTF();
if ("error".equals(result)) {
String reason = this.dis.readUTF();
LOGGER.info(reason);
code = "500";
message = reason;
} else if ("info".equals(result)) {
long l = this.dis.readLong();
LOGGER.info("======== 转换" ext "完成,文件大小(" l / 1048576L ")M ========");
LOGGER.info("======== 开始接受" ext " ========");
File newFile = new File(descRealPath);
if (newFile.exists()) {
newFile.delete();
}
this.fos = new FileOutputStream(newFile);
progress = 0L;
bytes = new byte[1048576];
while((length = this.dis.read(bytes, 0, bytes.length)) != -1) {
this.fos.write(bytes, 0, length);
this.fos.flush();
}
LOGGER.info("======== 接受" ext "文件成功========");
this.dis.close();
} else {
code = "500";
message = "链接被强制关闭....";
}
} else {
code = "404";
message = "文件不存在,或已被占用:" srcRealPath;
}
} catch (Exception e) {
code = "500";
message = "客户端报错:" e.toString();
LOGGER.error("异常:",e);
} finally {
if (this.fis != null) {
try {
this.fis.close();
} catch (Exception var38) {
;
}
}
if (this.fos != null) {
try {
this.fos.close();
} catch (Exception var37) {
;
}
}
try {
this.client.close();
} catch (Exception var36) {
;
}
}
rtnObject.element("code", code);
rtnObject.element("message", message);
return rtnObject;
}
public static void main(String[] args) {
try {
SocketClient socketClient = new SocketClient();
// 将文档转换成pdf文件
socketClient.processOffice("D:/2.doc","D:/2.pdf",YOZOOfficeUtil.PROCESS_TYPE_CONVERTFILE,null);
// 将文档转换成pdf文件
JSONObject dataObject = new JSONObject();
dataObject.element("bookmarkname","这个是测试呢日哦那个");
socketClient.processOffice("D:/2.doc","D:/2.pdf",YOZOOfficeUtil.PROCESS_TYPE_REPLACEBOOKMARK,dataObject);
} catch (Exception e) {
LOGGER.error("异常:",e);
}
}
}
5、启动word文件处理组件服务端
组件启动脚本
nohup ./ofdServer.sh &
6、调用服务端对word文件处理
public static void main(String[] args) {
try {
SocketClient socketClient = new SocketClient();
// 将文档转换成pdf文件
socketClient.processOffice("D:/2.doc","D:/2.pdf",YOZOOfficeUtil.PROCESS_TYPE_CONVERTFILE,null);
// 替换模板中的书签值,word中插入书签自行百度
JSONObject dataObject = new JSONObject();
dataObject.element("bookmarkname","这个是测试呢日哦那个");
socketClient.processOffice("D:/2.doc","D:/3.doc",YOZOOfficeUtil.PROCESS_TYPE_REPLACEBOOKMARK,dataObject);
} catch (Exception e) {
LOGGER.error("异常:",e);
}
}
7、资源下载
word文件处理组件服务端(开箱即用):
链接: https:///s/1_ZgjoX_nuv3a7_SKkJ_D7w 提取码: hn2r
服务端资源内容
将文件复制到linux服务器,并解压,执行 ./ofdServer.sh ,输出:服务端启动,监听端口为:8899,即运行成功
word文件处理组件客户端(开箱即用processOffice):
链接: https:///s/1mtabGY87RuAGGkwKrBIvfQ 提取码: mqxf
客户端资源文件内容
将源文件复制到项目指定包名,运行SocketClient.java中的main方法,可查看运行结果。
最重要的一点:服务器要安装永中OFFICE客户端
,