Java模板引擎生成word后转PDF效率优化

优化点:
  1. 模板预加载到内存(字节缓存),每次从内存中读取来生成模板,线程安全且减少IO
  2. 生成word后放到字节缓存中,避免中间文件IO
  3. 批量任务使用IO密集型线程池
核心代码:
import com.deepoove.poi.XWPFTemplate;
import org.docx4j.Docx4J;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import java.util.*;
import java.io.*;
import java.util.concurrent.*;

public class ConcurrentDocGenerator {

    // 1. 线程池配置(IO密集型任务)
    private static final ExecutorService executor = new ThreadPoolExecutor(
        4,                                     // corePoolSize
        Runtime.getRuntime().availableProcessors() * 2, // maxPoolSize
        60L, TimeUnit.SECONDS,
        new ArrayBlockingQueue<>(100),         // 防止无界队列导致OOM
        new CustomThreadFactory("doc-gen"),
        new ThreadPoolExecutor.CallerRunsPolicy()
    );

    // 2. 模板预加载(线程安全的字节缓存)
    private static final byte[] templateBytes;
    static {
        try (InputStream is = ConcurrentDocGenerator.class.getResourceAsStream("/template.docx")) {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len;
            while ((len = is.read(buffer)) > 0) {
                bos.write(buffer, 0, len);
            }
            templateBytes = bos.toByteArray();
        } catch (IOException e) {
            throw new RuntimeException("加载模板失败", e);
        }
    }

    // 3. 生成任务定义
    private static class DocGenTask implements Callable<byte[]> {
        private final Map<String, Object> data;

        DocGenTask(Map<String, Object> data) {
            this.data = new HashMap<>(data); // 防御性拷贝
        }

        @Override
        public byte[] call() throws Exception {
            // 3.1 从字节缓存创建模板(每个任务独立实例)
            try (InputStream is = new ByteArrayInputStream(templateBytes);
                 XWPFTemplate template = XWPFTemplate.compile(is).render(data)) {
                
                ByteArrayOutputStream wordBos = new ByteArrayOutputStream();
                template.write(wordBos);
                
                // 3.2 转换为PDF
                try (InputStream pdfIs = new ByteArrayInputStream(wordBos.toByteArray())) {
                    WordprocessingMLPackage pkg = WordprocessingMLPackage.load(pdfIs);
                    ByteArrayOutputStream pdfBos = new ByteArrayOutputStream();
                    Docx4J.toPDF(pkg, pdfBos);
                    return pdfBos.toByteArray();
                }
            }
        }
    }

    // 4. 批量处理方法
    public List<Future<byte[]>> batchGenerate(List<Map<String, Object>> dataList) {
        List<Future<byte[]>> futures = new ArrayList<>(dataList.size());
        for (Map<String, Object> data : dataList) {
            futures.add(executor.submit(new DocGenTask(data)));
        }
        return futures;
    }

    // 5. 自定义线程工厂(便于问题排查)
    private static class CustomThreadFactory implements ThreadFactory {
        private final String namePrefix;
        private final AtomicInteger threadNumber = new AtomicInteger(1);

        CustomThreadFactory(String poolName) {
            namePrefix = poolName + "-thread-";
        }

        public Thread newThread(Runnable r) {
            Thread t = new Thread(r, namePrefix + threadNumber.getAndIncrement());
            t.setDaemon(false);
            t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }

    // 关闭线程池(应用退出时调用)
    public static void shutdown() {
        executor.shutdown();
        try {
            if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }

    // 使用示例
    public static void main(String[] args) throws Exception {
        List<Map<String, Object>> testData = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            Map<String, Object> data = new HashMap<>();
            data.put("title", "文档-" + i);
            data.put("content", "这是第" + i + "个并发生成的文档");
            testData.add(data);
        }

        ConcurrentDocGenerator generator = new ConcurrentDocGenerator();
        List<Future<byte[]>> results = generator.batchGenerate(testData);

        // 获取结果(带超时机制)
        for (int i = 0; i < results.size(); i++) {
            try {
                byte[] pdfBytes = results.get(i).get(30, TimeUnit.SECONDS);
                try (FileOutputStream fos = new FileOutputStream("output-" + i + ".pdf")) {
                    fos.write(pdfBytes);
                }
                System.out.println("文档" + i + "生成成功");
            } catch (TimeoutException e) {
                System.err.println("文档" + i + "生成超时");
            } catch (ExecutionException e) {
                System.err.println("文档" + i + "生成失败: " + e.getCause().getMessage());
            }
        }
        
        shutdown();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值