当前位置: 首页 > news >正文

java实现预览服务器文件,不进行下载,并增加水印效果

通过文件路径获取文件,对不同类型的文件进行不同处理,将Word文件转成pdf文件预览,并早呢更加水印,暂不支持Excel文件,如果浏览器不支持PDF文件预览需要下载插件。文中currentUser.getUserid(),即为增加的水印内容,需要替换掉,字符串类型的。

加水印用的包是pdfbox,不是itext7,这两个包有的方法名一致,但是穿的参数类型不一致,引用的包不对,会报错。BufferedImage引用的是java.awt.image.BufferedImage引用成pdfbox的也会报错,有报错的参数注意一下引用的包。

<dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox</artifactId><version>2.0.24</version>
</dependency>
@RequestMapping("previewFile")public void download(String src,HttpServletRequest request,HttpServletResponse response) throws Exception {String filePath = src;System.out.println("filePath:" + filePath);File f = new File(filePath);if (!f.exists()) {response.sendError(404, "File not found!");return;}String fileName = f.getName();String extension = getFileExtension(f.getName());BufferedInputStream br = new BufferedInputStream(new FileInputStream(f));byte[] bs = new byte[1024];int len = 0;response.reset(); // 非常重要URL u = new URL("file:///" + filePath);//String contentType = u.openConnection().getContentType();String contentType = "";if(extension.equals("pdf")){contentType = "application/pdf";br = addWatermarkToPdf(filePath, currentUser.getUserid());}else if (extension.equals("txt")){contentType = "text/plain";}else if (extension.equals("doc") || extension.equals("docx")){contentType = "application/pdf";try {br = convertToPdf(filePath,currentUser.getUserid());}catch (Exception e){}}else if (extension.equals("jpg") || extension.equals("jpeg")){contentType = "image/jpeg";br = createWatermarkedImage(filePath, currentUser.getUserid());}else if (extension.equals("png")){contentType = "image/png";br = createWatermarkedImage(filePath, currentUser.getUserid());}else if (extension.equals("gif")){contentType = "image/gif";br = createWatermarkedImage(filePath, currentUser.getUserid());}//Document docsss = new Document(filePath);response.setContentType(contentType+"; charset=UTF-8");response.setHeader("Content-Disposition", "inline;filename="+ fileName);// 文件名应该编码成utf-8,注意:使用时,我们可忽略这句OutputStream out = response.getOutputStream();while ((len = br.read(bs)) > 0) {out.write(bs, 0, len);}out.flush();out.close();br.close();}public static BufferedInputStream createWatermarkedImage(String imagePath, String watermarkText) throws IOException {// 读取原始图片File file = new File(imagePath);BufferedImage targetImg = ImageIO.read(new FileInputStream(file));int height = targetImg.getHeight();int width = targetImg.getWidth();//-------------------------文字水印 start----------------------------BufferedImage waterMarkImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);// 创建画笔Graphics2D graphics2D = waterMarkImage.createGraphics();// 绘制原始图片graphics2D.drawImage(targetImg, 0, 0, width, height, null);// 设置水印颜色graphics2D.setColor(new Color(255, 255, 255, 255));double scale = 1.0;if(width < height){scale = (double) width / height;}int fontSize = (int) Math.ceil((double) (height / 25) * scale);// 设置字体 画笔字体样式为微软雅黑,加粗,文字大小按比例给graphics2D.setFont(new Font("微软雅黑", Font.BOLD, fontSize));float alpha = 0.7f; // 设置透明度为50%AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha);graphics2D.setComposite(composite);// 水印旋转度graphics2D.rotate(Math.toRadians(-25), (double) width / 2, (double) height / 2);int x = -width * 3;int y;// 计算水印文字的宽度FontMetrics fontMetrics = graphics2D.getFontMetrics();int watermarkWidth = fontMetrics.stringWidth(watermarkText);// 水印横向间隔int positionWidth = (int)(width * 0.12);// 水印竖向间隔int positionHeight  = (int)(height * 0.12);while (x < width * 3) {y = -height * 3;while (y < height * 3) {graphics2D.drawString(watermarkText, x, y);y += fontSize + positionWidth;}x += watermarkWidth + positionHeight;}graphics2D.dispose();ByteArrayOutputStream outputStream = new ByteArrayOutputStream();ImageIO.write(waterMarkImage, "png", outputStream);byte[] imageBytes = outputStream.toByteArray();return new BufferedInputStream(new ByteArrayInputStream(imageBytes));}public static BufferedInputStream addWatermarkToPdf(String filePath,String userId) throws IOException {// 加载PDF文档PDDocument document = PDDocument.load(new File(filePath));try {// 遍历所有页面for (PDPage page : document.getPages()) {// 获取页面的媒体框(即页面的大小)PDRectangle mediaBox = page.getMediaBox();float pageWidth = mediaBox.getWidth();float pageHeight = mediaBox.getHeight();// 创建内容流,使用APPEND模式PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true, true);// 设置水印字体和大小contentStream.setFont(PDType1Font.HELVETICA_BOLD, 36);// 设置水印颜色(RGB)contentStream.setNonStrokingColor(200, 200, 200);// 设置文本水印的参数String watermarkText = userId;PDType1Font font = PDType1Font.HELVETICA_BOLD;float fontSize = 20;float textWidth = font.getStringWidth(watermarkText) / 1000 * fontSize;float textHeight = fontSize;float xStep = pageWidth / 4; // 文本在x轴上的重复间隔float yStep = pageHeight / 12; // 文本在y轴上的重复间隔,可以根据需要调整// 绘制文本水印for (float y = 0; y < pageHeight; y += yStep) {for (float x = 0; x < pageWidth; x += xStep) {// 设置文本颜色(RGB),这里使用白色以确保在红色背景上可见contentStream.setStrokingColor(255, 255, 255);// 设置字体和大小contentStream.beginText();contentStream.setFont(font, fontSize);contentStream.newLineAtOffset(x, y);// 设置文本旋转//contentStream.newLineAtOffset(xx, yy);//contentStream.setTextMatrix(0, -0.5f, 0.5f, 0, 0, 0);contentStream.setTextMatrix(0.5f, 0.5f, -0.5f, 0.5f, x, y);contentStream.showText(watermarkText);contentStream.endText();}}// 关闭内容流contentStream.close();}ByteArrayOutputStream outputStream = new ByteArrayOutputStream();document.save(outputStream);document.close();return new BufferedInputStream(new ByteArrayInputStream(outputStream.toByteArray()));// 保存并关闭PDF文档//document.save(new File("watermarked.pdf"));//document.close();} finally {if (document != null) {document.close();}}}public static String getFileExtension(String fileName) {if (fileName == null || fileName.isEmpty()) {return "";}int dotIndex = fileName.lastIndexOf('.');if (dotIndex == -1 || dotIndex == fileName.length() - 1) {// No dot found or dot is at the end (e.g., "file.")return "";}return fileName.substring(dotIndex + 1);}public static BufferedInputStream convertToPdf(String wordFilePath,String userId) throws Exception {// 加载Word文档Document doc = new Document(wordFilePath);// 创建一个字节数组输出流来保存PDF内容ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();// 设置PDF保存选项(可选,这里使用默认设置)PdfSaveOptions saveOptions = new PdfSaveOptions();// 将文档保存为PDF格式到字节数组输出流中doc.save(byteArrayOutputStream, saveOptions);if (byteArrayOutputStream.size() == 0) {throw new IllegalArgumentException("ByteArrayOutputStream is empty");}// 将ByteArrayOutputStream的内容转换为ByteArrayInputStreamByteArrayInputStream bais = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());// 使用PDDocument.load方法将流转换为PDDocument对象PDDocument pdDocument = PDDocument.load(bais);// 关闭ByteArrayInputStreambais.close();// 将字节数组转换为字节输入流ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());// 返回BufferedInputStreamreturn addWatermarkToWord(pdDocument,userId);}public static BufferedInputStream addWatermarkToWord(PDDocument document,String userId) throws IOException {// 加载PDF文档//PDDocument document = PDDocument.load(new File(filePath));try {// 遍历所有页面for (PDPage page : document.getPages()) {// 获取页面的媒体框(即页面的大小)PDRectangle mediaBox = page.getMediaBox();float pageWidth = mediaBox.getWidth();float pageHeight = mediaBox.getHeight();// 创建内容流,使用APPEND模式PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true, true);// 设置水印字体和大小contentStream.setFont(PDType1Font.HELVETICA_BOLD, 36);// 设置水印颜色(RGB)contentStream.setNonStrokingColor(200, 200, 200);// 设置文本水印的参数String watermarkText = userId;PDType1Font font = PDType1Font.HELVETICA_BOLD;float fontSize = 20;float textWidth = font.getStringWidth(watermarkText) / 1000 * fontSize;float textHeight = fontSize;float xStep = pageWidth / 4; // 文本在x轴上的重复间隔float yStep = pageHeight / 12; // 文本在y轴上的重复间隔,可以根据需要调整// 绘制文本水印for (float y = 0; y < pageHeight; y += yStep) {for (float x = 0; x < pageWidth; x += xStep) {// 设置文本颜色(RGB),这里使用白色以确保在红色背景上可见contentStream.setStrokingColor(255, 255, 255);// 设置字体和大小contentStream.beginText();contentStream.setFont(font, fontSize);contentStream.newLineAtOffset(x, y);// 设置文本旋转//contentStream.newLineAtOffset(xx, yy);//contentStream.setTextMatrix(0, -0.5f, 0.5f, 0, 0, 0);contentStream.setTextMatrix(0.5f, 0.5f, -0.5f, 0.5f, x, y);contentStream.showText(watermarkText);contentStream.endText();}}// 关闭内容流contentStream.close();}ByteArrayOutputStream outputStream = new ByteArrayOutputStream();document.save(outputStream);document.close();return new BufferedInputStream(new ByteArrayInputStream(outputStream.toByteArray()));// 保存并关闭PDF文档//document.save(new File("watermarked.pdf"));//document.close();} finally {if (document != null) {document.close();}}}

相关文章:

java实现预览服务器文件,不进行下载,并增加水印效果

通过文件路径获取文件&#xff0c;对不同类型的文件进行不同处理&#xff0c;将Word文件转成pdf文件预览&#xff0c;并早呢更加水印&#xff0c;暂不支持Excel文件&#xff0c;如果浏览器不支持PDF文件预览需要下载插件。文中currentUser.getUserid()&#xff0c;即为增加的水…...

SAP月结、年结前重点检查事项(后勤与财务模块)

文章目录 一、PP生产模块相关的事务检查二、SD销售模块相关的事务检查:三、MM物料管理模块相关的事务检查四、FICO财务模块相关的事务检查五、年结前若干注意事项【SAP系统PP模块研究】 #SAP #生产订单 #月结 #年结 一、PP生产模块相关的事务检查 1、月末盘点后,生产用料的…...

MYSQL 高阶语句

目录 1、排列查询 2、区间判断 3、对结果进行分组查询 4、limit和distinct 5、设置别名 通配符 6、子查询 7、exists语句&#xff0c;判断子查询的结果是否为空 8、视图表 9、连接查询 1. 内连接 2. 左连接 3. 右连接 create table info ( id int primary key, name…...

VS Code中怎样查看某分支的提交历史记录

VsCode中无法直接查看某分支的提交记录&#xff0c;需借助插件才行&#xff0c;常见的插件如果git history只能查看某页面的改动记录&#xff0c;无法查看某分支的整体提交记录&#xff0c;我们可以安装GIT Graph插件来解决这个问题 1.在 VSCode的插件库中搜索 GIT Graph安装&a…...

知识库搭建实战一、(基于 Qianwen 大模型的知识库搭建)

基于 Qianwen 大模型的知识库开发规划 基础环境搭建可以参考文章:基础环境搭建 在构建智能应用时,知识库是一个重要的基础模块。以下将基于 Qianwen 大模型,详细介绍构建一个标准知识库的设计思路及其实现步骤。 知识库的核心功能模块 知识库开发的核心功能模块主要包括…...

ctr方法下载的镜像能用docker save进行保存吗?

ctr 和 docker 是两个不同的容器运行时工具,它们使用的镜像存储格式是兼容的(都是 OCI 标准镜像),但它们的镜像管理方式和存储路径不同。因此,直接使用 docker save 保存 ctr 拉取的镜像可能会遇到问题。 关键点 ctr 和 docker 的镜像存储位置不同: ctr(containerd)的镜…...

win32汇编环境下,窗口程序中生成listview列表控件及显示

;运行效果 ;抄下面源码在radasm里面&#xff0c;可以直接编译运行。重要部分加了备注。 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>&…...

运维之网络安全抓包—— WireShark 和 tcpdump

为什么要抓包&#xff1f;何为抓包&#xff1f; 抓包&#xff08;packet capture&#xff09;就是将网络传输发送与接收的数据包进行截获、重发、编辑、转存等操作&#xff0c;也用来检查网络安全。抓包也经常被用来进行数据截取等。为什么要抓包&#xff1f;因为在处理 IP网络…...

【复刻】数字化转型是否赋能企业新质生产力发展?(2015-2023年)

参照赵国庆&#xff08;2024&#xff09;的做法&#xff0c;对来自产业经济评论《企业数字化转型是否赋能企业新质生产力发展——基于中国上市企业的微观证据》一文中的基准回归部分进行复刻基于2015-2023年中国A股上市公司数据&#xff0c;实证分析企业数字化转型对新质生产力…...

【数据仓库】spark大数据处理框架

文章目录 概述架构spark 架构角色下载安装启动pyspark启动spark-sehll启动spark-sqlspark-submit经验 概述 Spark是一个性能优异的集群计算框架&#xff0c;广泛应用于大数据领域。类似Hadoop&#xff0c;但对Hadoop做了优化&#xff0c;计算任务的中间结果可以存储在内存中&a…...

2 秒杀系统架构

第一步 思考面临的问题和业务场景 秒杀系统面临的问题: 短时间内并发非常高&#xff0c;如果按照秒杀的并发做相应的承载会造成大量资源的浪费。第二解决超卖的问题。 第二步 思考目前的处境和解决方案 因为秒杀系统属于短时间内的高并发问题&#xff0c;我们不可能使用那么…...

UNI-APP_i18n国际化引入

官方文档&#xff1a;https://uniapp.dcloud.net.cn/tutorial/i18n.html vue2中使用 1. 新建文件 locale/index.js import en from ./en.json import zhHans from ./zh-Hans.json import zhHant from ./zh-Hant.json const messages {en,zh-Hans: zhHans,zh-Hant: zhHant }…...

【详解】AndroidWebView的加载超时处理

Android WebView的加载超时处理 在Android开发中&#xff0c;WebView是一个常用的组件&#xff0c;用于在应用中嵌入网页。然而&#xff0c;当网络状况不佳或页面加载过慢时&#xff0c;用户可能会遇到加载超时的问题。为了提升用户体验&#xff0c;我们需要对WebView的加载超时…...

RedisDesktopManager新版本不再支持SSH连接远程redis后

背景 RedisDesktopManager(又名RDM)是一个用于Windows、Linux和MacOS的快速开源Redis数据库管理应用程序。这几天从新下载RedisDesktopManager最新版本&#xff0c;结果发现新版本开始不支持SSH连接远程redis了。 解决方案 第一种 根据网上有效的信息&#xff0c;可以回退版…...

开源 SOAP over UDP

简介 看到有人想要实现两个 EXE 之间的互动。这可以采用 RPC 的方式嘛。 Delphi 现成的 RPC 框架&#xff0c;比如 WebService&#xff0c;比如 DataSnap&#xff1b; 当然&#xff0c;github 上面还有第三方开源的 XMLRPC 等等。 为啥要搞一个 UDP Delphi 的 WebService …...

Levenshtein 距离的原理与应用

引言 在文本处理和自然语言处理&#xff08;NLP&#xff09;中&#xff0c;衡量两个字符串相似度是一项重要任务。Levenshtein 距离&#xff08;也称编辑距离&#xff09;是一种常见的算法&#xff0c;用于计算将一个字符串转换为另一个字符串所需的最少编辑操作次数。这些操作…...

解决json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

前言 作者在读取json文件的时候出现上述报错&#xff0c;起初以为是自己json文件有问题&#xff0c;但借助在线工具查看后发现没问题&#xff0c;就卡住了&#xff0c;在debug的过程中发现了json文件读取的一个小坑&#xff0c;在此分享一下 解决过程 原代码 with open(anno…...

hive中的四种排序类型

1、Order by 全局排序 ASC&#xff08;ascend&#xff09;: 升序&#xff08;默认&#xff09; DESC&#xff08;descend&#xff09;: 降序 注意 &#xff1a;只有一个 Reducer,即使我们在设置set reducer的数量为多个,但是在执行了order by语句之后,当前此次的运算还是只有…...

Spring-AI讲解

Spring-AI langchain(python) langchain4j 官网&#xff1a; https://spring.io/projects/spring-ai#learn 整合chatgpt 前置准备 open-ai-key: https://api.xty.app/register?affPuZD https://xiaoai.plus/ https://eylink.cn/ 或者淘宝搜&#xff1a; open ai key魔法…...

【brew安装失败】DNS 查询 raw.githubusercontent.com 返回的是 0.0.0.0

从你提供的 nslookup 输出看&#xff0c;DNS 查询 raw.githubusercontent.com 返回的是 0.0.0.0&#xff0c;这通常意味着无法解析该域名或该域名被某些 DNS 屏蔽了。这种情况通常有几个可能的原因&#xff1a; 可能的原因和解决方法 本地 DNS 问题&#xff1a; 有可能是你的本…...

srs linux

下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935&#xff0c;SRS管理页面端口是8080&#xff0c;可…...

【JavaSE】绘图与事件入门学习笔记

-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角&#xff0c;以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向&#xff0c;距离坐标原点x个像素;第二个是y坐标&#xff0c;表示当前位置为垂直方向&#xff0c;距离坐标原点y个像素。 坐标体系-像素 …...

什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南

文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/55aefaea8a9f477e86d065227851fe3d.pn…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

c++第七天 继承与派生2

这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分&#xff1a;派生类构造函数与析构函数 当创建一个派生类对象时&#xff0c;基类成员是如何初始化的&#xff1f; 1.当派生类对象创建的时候&#xff0c;基类成员的初始化顺序 …...

Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storms…...

【LeetCode】算法详解#6 ---除自身以外数组的乘积

1.题目介绍 给定一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O…...

6️⃣Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙

Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙 一、前言:离区块链还有多远? 区块链听起来可能遥不可及,似乎是只有密码学专家和资深工程师才能涉足的领域。但事实上,构建一个区块链的核心并不复杂,尤其当你已经掌握了一门系统编程语言,比如 Go。 要真正理解区…...

macOS 终端智能代理检测

&#x1f9e0; 终端智能代理检测&#xff1a;自动判断是否需要设置代理访问 GitHub 在开发中&#xff0c;使用 GitHub 是非常常见的需求。但有时候我们会发现某些命令失败、插件无法更新&#xff0c;例如&#xff1a; fatal: unable to access https://github.com/ohmyzsh/oh…...