当前位置: 首页 > 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; 有可能是你的本…...

Qwen3-14B在Keil5 MDK开发中的奇思妙用:注释生成与调试日志分析

Qwen3-14B在Keil5 MDK开发中的奇思妙用&#xff1a;注释生成与调试日志分析 1. 嵌入式开发的痛点与AI机遇 在STM32项目开发过程中&#xff0c;每个工程师都经历过这样的场景&#xff1a;接手一个遗留项目&#xff0c;面对大段没有注释的汇编代码&#xff1b;或者调试时串口不…...

适配器模式设计思路

01.适配器模式基础适配器模式是一种结构型设计模式&#xff0c;用于将不兼容的接口转换为可兼容的接口&#xff0c;使原本不能一起工作的类可以协同工作。本文详细介绍了适配器模式的基础、实现方式&#xff08;类适配器和对象适配器&#xff09;、应用场景&#xff08;如封装有…...

像素幻梦应用场景:独立开发者快速构建像素风APP启动页与加载动画

像素幻梦应用场景&#xff1a;独立开发者快速构建像素风APP启动页与加载动画 1. 为什么独立开发者需要像素幻梦 在移动应用市场竞争激烈的今天&#xff0c;一个独特的视觉风格往往能成为APP脱颖而出的关键。对于独立开发者而言&#xff0c;设计精美的启动页和加载动画不仅能提…...

学术研究必备:8款AI论文写作工具,爱毕业aibiye高效实用

人工智能技术在学术研究领域的深度整合为论文撰写流程带来了革命性变革&#xff0c;通过8款核心智能工具的协同应用——包括文献智能分析系统、自动化内容生成引擎以及文本精准优化平台——研究者能够实现从数据挖掘到学术表达的全程智能化&#xff0c;显著提升文献处理效率与学…...

基于Python的电影订票系统毕业设计源码

博主介绍&#xff1a;✌ 专注于Java,python,✌关注✌私信我✌具体的问题&#xff0c;我会尽力帮助你。一、研究目的本研究旨在设计并实现一个基于Python的电影订票系统&#xff0c;以满足现代观众对于便捷、高效电影购票服务的需求。具体研究目的如下&#xff1a; 首先&#xf…...

直接可用4轴插补算法库,STM32的DDA插补联动与梯形加减速算法代码

可以直接使用的4轴插补算法库&#xff0c;不是丢给你一堆gr1b或者写字机或者3d打印的开源代码&#xff0c;本运控库上项目级别的&#xff0c;需要添加在自己的项目中&#xff0c;不支持gm码&#xff0c;只有运动控制核心代码&#xff0c;可以添加在自己项目中的&#xff0c;stm…...

计算机毕业设计:Python汽车销量数据挖掘与预测系统 Flask框架 scikit-learn 可视化 requests爬虫 AI 大模型(建议收藏)✅

博主介绍&#xff1a;✌全网粉丝10W,前互联网大厂软件研发、集结硕博英豪成立工作室。专注于计算机相关专业项目实战6年之久&#xff0c;选择我们就是选择放心、选择安心毕业✌ > &#x1f345;想要获取完整文章或者源码&#xff0c;或者代做&#xff0c;拉到文章底部即可与…...

接cst-matlab自动化建模,cst天线/超表面数据集自动化计算和收集,提供建模代码

接cst-matlab自动化建模&#xff0c;cst天线/超表面数据集自动化计算和收集&#xff0c;提供建模代码&#xff0c;提供数据集数据CST和MATLAB这对组合最近被我玩出花了。搞天线设计的朋友应该都懂&#xff0c;手动建模调参简直是精神折磨——尤其是超表面这种动辄几十个单元的结…...

HC32F460引脚复用避坑指南:如何正确释放SWDIO/SWCLK做普通IO

HC32F460引脚复用实战&#xff1a;释放SWDIO/SWCLK的完整解决方案 当你在华大HC32F460项目中发现GPIO资源紧张时&#xff0c;PB3/PB4这些复用引脚就像藏在抽屉里的备用钥匙。但当你真正需要使用它们时&#xff0c;却发现这些引脚被调试接口牢牢占据。这不是简单的配置问题&…...

论文AI率太高怎么降?去AI化实用技巧与工具避坑指南

“整篇论文都是自己原创的&#xff0c;就用AI顺了下逻辑&#xff0c;结果学校AIGC检测直接飙到73%&#xff0c;当场被打回”“熬了3个通宵手动改&#xff0c;AI率才降了不到12%&#xff0c;离答辩只剩一周根本赶不完”“随便找了个降AI工具&#xff0c;把我专业名词改得乱七八糟…...