文件转换工具类—基于jodconverter和pdfbox实现的可以自定义各类文件转换和水印

news2024/11/15 13:44:07

源码获取:原文地址

概览


需要依赖

<dependency>
    <groupId>org.jodconverter</groupId>
    <artifactId>jodconverter-local</artifactId>
    <version>4.4.6</version>
</dependency>
<dependency>
    <groupId>org.apache.pdfbox</groupId>
    <artifactId>pdfbox</artifactId>
    <version>2.0.28</version>
</dependency>

核心实现

文件转换

文件转换的核心分为两步:

  • 启动officeManager的LocalConverter
  • 使用LocalConverter的converter()进行文件转换
    private LocalConverter newLocalConverter() throws OfficeException {
        if (StringUtils.isBlank(officeHome)) {
            File defHome = LocalOfficeUtils.getDefaultOfficeHome();
            if (defHome.exists() && defHome.isDirectory()) {
                officeHome = defHome.toString();
            }
        }
        if (StringUtils.isBlank(officeHome)) {
            logger.error("未指定 LibraryOffice 安装目录 (office.home)");
            return null;
        }
        LocalOfficeManager officeManager = LocalOfficeManager.builder()
                .maxTasksPerProcess(3)
                .existingProcessAction(ExistingProcessAction.KILL)
                .processTimeout(120_000L)
                .taskExecutionTimeout(30_000L)
                .taskQueueTimeout(30_000L)
                .officeHome(officeHome)
                .build();
        officeManager.start();
        SpringApplication.getShutdownHandlers().add(() -> {
            if (officeManager.isRunning()) {
                try {
                    officeManager.stop();
                } catch (Throwable e) {
                    //ignore
                }
            }
        });
        return LocalConverter.builder().officeManager(officeManager).build();
    }

通过 LocalOfficeUtils.getDefaultOfficeHome() 可以获取到安装好的Libreoffice的路径,使用 LocalOfficeManager.builder() 构建 LocalOfficeManager 并启动,SpringApplication.getShutdownHandlers().add() 注册一个钩子,在程序关闭的时候结束掉 officeManager 。

    private void covert(InputStream source, OutputStream target, FileType formType, FileType toType, WaterMark waterMark) throws FileConversionException {
        DocumentFormat srcType = DefaultDocumentFormatRegistry.getFormatByExtension(formType.name());
        DocumentFormat trgType = DefaultDocumentFormatRegistry.getFormatByExtension(toType.name());
        if (srcType == null || trgType == null) {
            throw new FileConversionException("不支持该文件格式转换: " + formType + "-->" + toType);
        }
        try {
            if (waterMark != null) {
                File tempFile = File.createTempFile("convert-", ".pdf");
                logger.info("生成临时文件:{}", tempFile);
                converter.convert(source).as(srcType).to(tempFile).as(trgType).execute();
                waterMark.add(new FileInputStream(tempFile), target);
                if (!tempFile.delete()) logger.error("临时文件删除失败:{}", tempFile);
            } else {
                converter.convert(source).as(srcType).to(target).as(trgType).execute();
            }
        } catch (OfficeException | IOException e) {
            throw new FileConversionException(e.getMessage(), e.getCause());
        }
    }

使用 LocalConverter.builder().officeManager(officeManager).build()返回的 converter 进行转换,对于需要加水印的pdf,传入实现WaterMark接口的实现类可以自定义水印。

以文本水印为列:

    @Override
    public void add(InputStream in, OutputStream out) throws IOException {
        // 加载PDF文件fontSize
        PDDocument document = PDDocument.load(in);
        document.setAllSecurityToBeRemoved(true);
        // 遍历PDF文件,在每一页加上水印
        for (PDPage page : document.getPages()) {
            PDPageContentStream stream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true, true);
            // 加载水印字体
            PDFont font = PDType1Font.COURIER_BOLD;
            PDExtendedGraphicsState r = new PDExtendedGraphicsState();
            // 设置透明度
            r.setNonStrokingAlphaConstant(0.2f);
            r.setAlphaSourceFlag(true);
            stream.setGraphicsStateParameters(r);
            // 设置水印字体颜色
            stream.setFont(font, 12);
            stream.setNonStrokingColor(0, 200, 0);
            stream.beginText();
            stream.newLineAtOffset(0, -15);
            // 获取PDF页面大小
            float pageHeight = page.getMediaBox().getHeight();
            float pageWidth = page.getMediaBox().getWidth();
            // 根据纸张大小添加水印,30度倾斜
            for (int h = 10; h < pageHeight; h = h + rowSpace) {
                for (int w = -10; w < pageWidth; w = w + colSpace) {
                    stream.setTextMatrix(Matrix.getRotateInstance(0.3, w, h));
                    stream.showText(waterMark);
                }
            }
            // 结束渲染,关闭流
            stream.endText();
            stream.restoreGraphicsState();
            stream.close();
        }
        document.save(out);
        document.close();
        in.close();
    }

实现 WaterMark 的 add() 完成自定义,PDDocument.load()document.save()既可以传File,也可以传 InputStream,根据需求改造即可。

其他工具类定义

  • 用于下载转换后的文件时设置response的参数
/**
 * @author Justin
 */
public class HttpUtil {

    public static void responseSetting(String fileName, String extension, HttpServletResponse response) {
        String baseName = FilenameUtils.getBaseName(fileName).replace(" ", "");
        if (StringUtils.isBlank(extension)) extension = FilenameUtils.getExtension(fileName);
        String fileNameURL = URLEncoder.encode(baseName + "." + extension, StandardCharsets.UTF_8);
        //设置响应类型:传输内容是流,并支持中文
        response.setContentType("application/octet-stream;charset=UTF-8");
        //设置响应头信息header,下载时以文件附件下载
        response.setHeader("Content-disposition", "attachment;filename=" + fileNameURL + ";" + "filename*=utf-8''" + fileNameURL);
    }
}

使用示例:

    @PostMapping("/convert_new")
    public void covert_new(@RequestParam("file") MultipartFile file, HttpServletResponse response) {
        try {
            InputStream is = file.getInputStream();
            HttpUtil.responseSetting(file.getOriginalFilename(), "pdf", response);
            fileConvertService.word2Pdf(is, response.getOutputStream(), TextWaterMark.Builder().waterMark("Justin"));
        } catch (IOException | FileConversionException e) {
            e.printStackTrace();
        }

    }

可能出现错误

The field file exceeds its maximum permitted size of 1048576 bytes

原因:springboot内置的tomcat默认上传文件大小为1mb,需要修改配置文件application.yml,添加:

spring:
  servlet:
    multipart:
      max-file-size: 50MB
      max-request-size: 50MB

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/656582.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【MyBatis学习】占位符,sql注入问题,like模糊匹配等可能出现一定的问题,赶快与我一同去了解,避免入坑吧 ! ! !

前言: 大家好,我是良辰丫,今天还是我们的mybatis的学习,主要内容有两个占位符,sql注入问题,like模糊匹配,以及多表查询等,不断提升我们的编程能力,加油哈! ! !&#x1f48c;&#x1f48c;&#x1f48c; &#x1f9d1;个人主页&#xff1a;良辰针不戳 &#x1f4d6;所属专栏&…

MP地面站下载和回放日志

参考 https://ardupilot.org/dev/docs/common-downloading-and-analyzing-data-logs-in-mission-planner.html#common-downloading-and-analyzing-data-logs-in-mission-planner 下载日志 首先连接上飞控 然后在下图页面下载日志&#xff1a; 点击下图下载日志 下载的日志会…

在CentOS 7上安装Python 3.9

前言 这是我在这个网站整理的笔记&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&#xff1a;RodmaChen 在CentOS 7上安装Python 3.9 一. 更新系统软件包二. 安装必要的软件包和依赖项三. 下载Python 3.9四. 解压和编译源代码五. 安装Python 3.9六. 验证安装 一. 更…

SpringCloud Alibaba-Seata分布式事务

SpringCloud Alibaba-Seata 1 常用事务解决方案模型1.1 DTP模型1.2 2PC1.3 3PC1.4 TCC 2 Seata2.1 Seata术语2.1 Seata AT模式2.1.1 AT模式及工作流程2.1.2 Seata-Server安装2.1.3 集成springcloud-alibaba 4.2 Seata TCC模式 3 Seata注册中心3.1 服务端注册中心配置3.2 客户端…

全国主要城市建筑轮廓(含层高)矢量数据分享及最新AI提取建筑分布方法介绍

今天要给大家带来的数据就是全国主要大中型城市的城市建筑轮廓矢量数据&#xff01;&#xff01;同时给大家一个傻瓜式的建筑物提取软件&#xff0c;以及其使用方法&#xff01;&#xff01; 第一部分&#xff1a;数据 一、数据基本情况 建筑轮廓数据实际上就是建筑的边界矢量…

easyX绘图设备相关函数(注释版)

0.前言 这里是limou3434的easyX博文系列&#xff0c;感兴趣可以看看我的其他内容。 本次我给您带来的是easyX的绘图设备相关函数&#xff0c;和上一篇一样&#xff0c;对于官方文档我给了一些自认为重要的注释和测试例子&#xff0c;来辅助您理解这些函数。 1.easyX库函数分…

【汤4操作系统】深入掌握操作系统-文件管理篇

第六章 文件管理 文件 数据项&记录&文件 数据项分为&#xff1a; 基本数据项&#xff1a;描述对象的某些属性&#xff0c;例如学生的年龄&#xff0c;姓名学号等组合数据项&#xff1a;由若干个基本数据项组合而成 记录&#xff1a;一组相关数据项的集合&#xff0…

光线追踪中的空间划分,辐射度量学简介

之前接触过四岔树&#xff0c;这里用到了KD-tree和BSP-Tree KD-Tree 对于如何划分&#xff1a; 首先需要知道需要沿着哪一条轴进行划分&#xff0c;划分的位置所有节点不存在父节点上&#xff0c;只存在于叶节点上 对于如何查找 光线穿过包围盒A&#xff0c;那么分别对其两…

4.4 超简单文书编辑器:nano

在Linux系统当中有非常多的文书编辑器存在&#xff0c;其中最重要的是vim。 nano使用很简单&#xff0c;可以直接加上文件名就能够打开一个旧文件或新文件。打开一个叫text.txt的文件名来看看&#xff1a; [ctrl]-G&#xff1a;取得线上说明&#xff08;help&#xff09;&…

Python案例——采集专栏文章保存成pdf

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 环境使用: python 3.8 >>>>>> 运行代码 pycharm 2022.3 >>>>>> 辅助敲代码 wkhtmltopdf 软件 找助理邀课老师获取 模块使用: 内置模块 re >>>正则表达式 第三方模…

es6 的模块化由来

es 模块化&#xff0c;之前是没有的。没有的时候&#xff0c;用的是社区创建的commjs模块化 commjs模块其实是一个对象&#xff0c;这个对象要在代码运行的时候才会创建出来的 这有个不好的地方&#xff0c;就是不能在编译的时候找到依赖文件&#xff0c;也不能进行类型检查&…

一文搞定C++异常机制(附代码+详细解析)

C异常 1.引文C语言传统的处理错误的方式&#xff1a; 2.C异常概念3.异常的使用3.1 异常的抛出和捕获3.2 异常的重新抛出异常捕获中的内存泄漏问题 3.3异常安全3.4异常规范 4.异常优缺点5.总结&#xff1a; 1.引文 C语言传统的处理错误的方式&#xff1a; 终止程序&#xff0c…

【GAI】红杉美国生成式AI:一个创造性的新世界

The New Language Model Stack 红杉美国官网发表了最新一篇题为《Generative AI: A Creative New World》的文章译稿&#xff0c;&#xff0c;原文作者是红杉的两位合伙人&#xff1a;Sonya Huang和Pat Grady&#xff0c;有意思的是在文章作者一栏&#xff0c;赫然还写着GPT-3…

华为OD机试真题 JavaScript 实现【磁盘容量排序】【2022Q4 100分】,附详细解题思路

一、题目描述 磁盘的容量单位常用的有M&#xff0c;G&#xff0c;T这三个等级&#xff0c;它们之间的换算关系为1T 1024G&#xff0c;1G 1024M&#xff0c;现在给定n块磁盘的容量&#xff0c; 请对它们按从小到大的顺序进行稳定排序&#xff0c;例如给定5块盘的容量&#x…

宠物电商社区APP的设计与实现

摘 要&#xff1a;为了设计并实现具有宠物产品商城和问答社区功能的APP&#xff0c;通过比较国内外宠物行业电商发展现状和国内已有的宠物电商平台的优缺点&#xff0c;分析可行性和需求&#xff0c;从而进行详细设计和实现。该APP后台采用SSM框架&#xff0c;数据库使用MySQL…

最终版:1分钟自动部署数字人平台并提供web服务:唇形合成(wav2lip) + 超分修复(codeformer),

Demo效果 本文实现步骤:数字人形象(AI绘画) -> 文字转语音(谷歌tts) -> 表情迁移 -> 唇形合成 -> 视频超分 上述步骤所有技术均已在此专栏发布,可点击上方专栏查看具体博文 所有技术依赖环境及api接口均封装打包完毕,使用docker一键部署,预计耗时10分钟 原图 …

tolua源码分析(八)lua扩展继承C#类

tolua源码分析&#xff08;八&#xff09;lua扩展继承C#类 上一节我们阐述了lua调用带out参数的C#函数机制&#xff0c;本节我们来看下lua层是如何扩展C#类的。这次的例子在example 17&#xff0c;主要都是lua代码&#xff1a; LuaTransform { } …

1.pixi.js编写的塔防游戏(类似保卫萝卜)-设计逻辑

游戏说明 一个用pixi.js编写的h5塔防游戏&#xff0c;可以用electron打包为exe&#xff0c;支持移动端&#xff0c;也可以用webview控件打包为app在移动端使用 环境说明 cnpm6.2.0 npm6.14.13 node12.22.7 npminstall3.28.0 yarn1.22.10 npm config list electron_mirr…

1.2 聚合工程与springboot整合

步骤1&#xff1a; 在顶级父工程中添加springboot相关的pom配置 <!--springboot父级依赖&#xff0c;表示是一个是springboot项目 --> <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifa…

vue3(基于vite)项目初始基本配置

简介 一个好的项目开始搭建总是需要配置许多初始化配置&#xff0c;比如eslint语法检验、prettier代码格式统一、husky做commit拦截等等&#xff0c;本文从零开始带你一步步搭建一个完整的项目配置&#xff0c;熟悉之后下次直接拿来即用 环境准备 node v16以上pnpm 8.0.0 1.新…