更新类名
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
package com.optima.document.server;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class JodConverterDocumentServer {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(JodConverterDocumentServer.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
package com.optima.document.server.api;
|
||||
|
||||
import com.deepoove.poi.XWPFTemplate;
|
||||
import com.deepoove.poi.config.Configure;
|
||||
import com.deepoove.poi.data.PictureRenderData;
|
||||
import com.deepoove.poi.data.TextRenderData;
|
||||
import com.deepoove.poi.policy.ListRenderPolicy;
|
||||
import com.deepoove.poi.policy.PictureRenderPolicy;
|
||||
import com.deepoove.poi.render.RenderContext;
|
||||
import com.deepoove.poi.template.run.RunTemplate;
|
||||
import com.deepoove.poi.xwpf.BodyContainer;
|
||||
import com.deepoove.poi.xwpf.BodyContainerFactory;
|
||||
import com.optima.document.api.DocumentService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.rendering.ImageType;
|
||||
import org.apache.pdfbox.rendering.PDFRenderer;
|
||||
import org.apache.pdfbox.tools.imageio.ImageIOUtil;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFRun;
|
||||
import org.jodconverter.core.DocumentConverter;
|
||||
import org.jodconverter.core.document.DefaultDocumentFormatRegistry;
|
||||
import org.jodconverter.core.document.DocumentFormat;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 服务接口实现
|
||||
*
|
||||
* @author Elias
|
||||
* @since 2021-09-28 16:18
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class DocumentServiceImpl implements DocumentService {
|
||||
@Resource
|
||||
private DocumentConverter documentConverter;
|
||||
|
||||
/**
|
||||
* word模版引擎配置
|
||||
*/
|
||||
private static final Configure wtlConfig = Configure.builder().buildGramer("${", "}")
|
||||
.setValidErrorHandler(new Configure.DiscardHandler())
|
||||
.addPlugin('%', new ListRenderPolicy() {
|
||||
@Override
|
||||
public void doRender(RenderContext<List<Object>> context) throws Exception {
|
||||
XWPFRun run = context.getRun();
|
||||
List<?> dataList = context.getData();
|
||||
Iterator<?> var5 = dataList.iterator();
|
||||
while (var5.hasNext()) {
|
||||
Object data = var5.next();
|
||||
if (data instanceof TextRenderData) {
|
||||
run.setText(((TextRenderData) data).getText());
|
||||
if (var5.hasNext()) {
|
||||
run.setText(",");
|
||||
}
|
||||
} else if (data instanceof PictureRenderData) {
|
||||
PictureRenderPolicy.Helper.renderPicture(run, (PictureRenderData) data);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
.setRenderDataComputeFactory(envModel ->
|
||||
el -> {
|
||||
Object data = envModel.getRoot();
|
||||
if ("#this".equals(el)) {
|
||||
return data;
|
||||
} else if (data instanceof Map) {
|
||||
Map dataMap = ((Map) data);
|
||||
if (dataMap.containsKey(el)) {
|
||||
return dataMap.get(el);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.build();
|
||||
|
||||
/**
|
||||
* 文件格式转换
|
||||
*
|
||||
* @param source 源文件流
|
||||
* @param sourceExtension 源文件后缀名,不包含"."
|
||||
* @param targetExtension 目标文件后缀名
|
||||
* @return 转换后的文件流
|
||||
*/
|
||||
public byte[] convert(byte[] source, String sourceExtension, String targetExtension) {
|
||||
try {
|
||||
sourceExtension = sourceExtension.replace(".", "");
|
||||
targetExtension = targetExtension.replace(".", "");
|
||||
long start = System.currentTimeMillis();
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DocumentFormat sourceFormat = DefaultDocumentFormatRegistry.getFormatByExtension(sourceExtension);
|
||||
DocumentFormat targetFormat = DefaultDocumentFormatRegistry.getFormatByExtension(targetExtension);
|
||||
assert sourceFormat != null;
|
||||
assert targetFormat != null;
|
||||
documentConverter.convert(new ByteArrayInputStream(source))
|
||||
.as(sourceFormat)
|
||||
.to(bos)
|
||||
.as(targetFormat)
|
||||
.execute();
|
||||
log.info("convert {} to {} take time in millis:{}", sourceExtension, targetExtension, System.currentTimeMillis() - start);
|
||||
return bos.toByteArray();
|
||||
} catch (Exception e) {
|
||||
log.error("convert {} to {} error", sourceExtension, targetExtension, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public byte[] convert(byte[] source, String sourceExtension, String targetExtension, String targetFormat) {
|
||||
return convert(source, sourceExtension, targetExtension);
|
||||
}
|
||||
|
||||
public byte[] generateWord(byte[] sourceTemplate, Map<String, Object> dataModel) {
|
||||
long start = System.currentTimeMillis();
|
||||
XWPFTemplate template = XWPFTemplate.compile(new ByteArrayInputStream(sourceTemplate), wtlConfig).render(dataModel);
|
||||
try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
|
||||
template.write(bos);
|
||||
log.info("word generate take time in millis:{}", System.currentTimeMillis() - start);
|
||||
return bos.toByteArray();
|
||||
} catch (Exception e) {
|
||||
log.error("word generate error", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] wordToPdf(byte[] source, String sourceFormat, boolean clear) {
|
||||
return wordToPdf(source, clear);
|
||||
}
|
||||
|
||||
public byte[] wordToPdf(byte[] source, boolean clear) {
|
||||
try {
|
||||
long start = System.currentTimeMillis();
|
||||
if (clear) {
|
||||
try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
|
||||
XWPFTemplate template = XWPFTemplate.compile(new ByteArrayInputStream(source), wtlConfig);
|
||||
BodyContainer bodyContainer = BodyContainerFactory.getBodyContainer(template.getXWPFDocument());
|
||||
template.getElementTemplates().forEach(it -> {
|
||||
if (it instanceof RunTemplate) {
|
||||
RunTemplate rt = (RunTemplate) it;
|
||||
bodyContainer.clearPlaceholder(rt.getRun());
|
||||
}
|
||||
});
|
||||
template.writeAndClose(bos);
|
||||
source = bos.toByteArray();
|
||||
log.info("clear placeholder take time in millis:{}", System.currentTimeMillis() - start);
|
||||
} catch (Exception e) {
|
||||
log.error("clear placeholder error", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return convert(source, "docx", "pdf");
|
||||
} catch (Exception e) {
|
||||
log.error("word to pdf error", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] wordToImage(byte[] source, String targetExtension) {
|
||||
try {
|
||||
byte[] pdfBytes = wordToPdf(source, true);
|
||||
long start = System.currentTimeMillis();
|
||||
PDDocument document = PDDocument.load(new ByteArrayInputStream(pdfBytes));
|
||||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||
BufferedImage bim = pdfRenderer.renderImageWithDPI(0, 300, ImageType.RGB);
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ImageIOUtil.writeImage(bim, targetExtension, bos, 300);
|
||||
document.close();
|
||||
log.info("word to image take time in millis:{}", System.currentTimeMillis() - start);
|
||||
return bos.toByteArray();
|
||||
} catch (Exception e) {
|
||||
log.error("word to image error", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public byte[] wordToImage(byte[] source, String sourceExtension, String targetExtension) {
|
||||
return wordToImage(source, targetExtension);
|
||||
}
|
||||
|
||||
public byte[] docToDocx(byte[] source) {
|
||||
return convert(source, "doc", "docx");
|
||||
}
|
||||
|
||||
public byte[] xlsToXlsx(byte[] source) {
|
||||
return convert(source, "xls", "xlsx");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.optima.document.server.config;
|
||||
|
||||
import com.optima.document.api.DocumentService;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter;
|
||||
|
||||
/**
|
||||
* 服务端配置
|
||||
*
|
||||
* @author Elias
|
||||
* @since 2021-09-28 16:12
|
||||
*/
|
||||
@Configuration
|
||||
public class DocumentConfig {
|
||||
/**
|
||||
* 文档接口
|
||||
*
|
||||
* @return httpinvoker
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
@Bean(name = "/document-service")
|
||||
HttpInvokerServiceExporter wordService(DocumentService documentService) {
|
||||
HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter();
|
||||
exporter.setService(documentService);
|
||||
exporter.setServiceInterface(DocumentService.class);
|
||||
return exporter;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
server:
|
||||
port: 9004
|
||||
|
||||
jodconverter:
|
||||
local:
|
||||
# 启动本地转换
|
||||
enabled: true
|
||||
# macOS下:program/soffice 的 program 所在目录 或 MacOS/soffice 的 MacOS 所在目录
|
||||
# windows下:program/soffice.exe 的 program 所在目录
|
||||
# linux下:program/soffice.bin 的 program 所在目录
|
||||
# 如果不配置,则自动查找
|
||||
office-home: /Applications/LibreOffice.app/Contents
|
||||
# 一个端口表示一个常驻进程,默认只有一个进程,端口为2002
|
||||
port-numbers: [ 2002 ]
|
||||
# 每个进程最多处理多个任务,默认为200
|
||||
max-tasks-per-process: 200
|
||||
@@ -0,0 +1,6 @@
|
||||
██████╗ ██████╗ ██████╗██╗ ██╗███╗ ███╗███████╗███╗ ██╗████████╗
|
||||
██╔══██╗██╔═══██╗██╔════╝██║ ██║████╗ ████║██╔════╝████╗ ██║╚══██╔══╝
|
||||
██║ ██║██║ ██║██║ ██║ ██║██╔████╔██║█████╗ ██╔██╗ ██║ ██║
|
||||
██║ ██║██║ ██║██║ ██║ ██║██║╚██╔╝██║██╔══╝ ██║╚██╗██║ ██║
|
||||
██████╔╝╚██████╔╝╚██████╗╚██████╔╝██║ ╚═╝ ██║███████╗██║ ╚████║ ██║
|
||||
╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝ ╚═╝
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.optima.document.server;
|
||||
|
||||
import com.optima.document.api.DocumentService;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Disabled
|
||||
public class JodConverterDocumentServerTest {
|
||||
|
||||
@Test
|
||||
public void test() throws IOException {
|
||||
DocumentService documentService = buildDocumentService();
|
||||
|
||||
File sourceDocx = new File("/Users/yanghuanglin/Downloads/test.docx");
|
||||
File targetPdf = new File("/Users/yanghuanglin/Downloads/test.pdf");
|
||||
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("callerName", "张三");
|
||||
byte[] sourceBytes = documentService.generateWord(Files.readAllBytes(sourceDocx.toPath()), params);
|
||||
|
||||
byte[] bytes = documentService.wordToPdf(sourceBytes, false);
|
||||
FileUtils.writeByteArrayToFile(targetPdf, bytes);
|
||||
}
|
||||
|
||||
private static DocumentService buildDocumentService() {
|
||||
// 创建客户端代理
|
||||
HttpInvokerProxyFactoryBean factoryBean = new HttpInvokerProxyFactoryBean();
|
||||
factoryBean.setServiceUrl("http://127.0.0.1:9005/document-service");
|
||||
factoryBean.setServiceInterface(DocumentService.class);
|
||||
factoryBean.afterPropertiesSet();
|
||||
|
||||
return (DocumentService) factoryBean.getObject();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user