word文档转为pdf, 并加水印
word文档转为pdf, 并加水印
说明:
项目中有这样一个需求:上传的word文档要转为pdf格式,下载时加水印,并且要求文件内容只能看不能写。
思路:
- 上传
- word文档转为pdf格式
- pdf文档转为一张一张的图片
- 再把图片集合写入一个新的pdf文件中 (这里可以存到项目中的文件服务器上)
- 下载
- 上一步的pdf文件中加水印生成一个新的pdf
- 下载上一步的pdf文件
下面贴上代码
1、加入依赖
<!-- 对PDF文件的操作 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13.1</version>
</dependency>
<!-- PDF文件 字体 防止中文乱码 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<!-- pdfBox -->
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.24</version>
</dependency>
<dependency>
<groupId>com.aspose</groupId>
<artifactId>aspose.slides</artifactId>
<version>15.11.0</version>
</dependency>
<dependency>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
<version>2.0.8</version>
<exclusions>
<exclusion>
<groupId>bouncycastle</groupId>
<artifactId>bcprov-jdk14</artifactId>
</exclusion>
</exclusions>
</dependency>
2、上传时处理
这里使用fastDfs保存文件流(项目中加入fastdfs这里就不做说明了)
@Autowired
private FastFileStorageClient fastFileStorageClient;
@PostMapping(value = "/save_knowledge_file")
public String saveKnowledgeFile(HttpServletRequest request, HttpServletResponse response) {
response.addHeader("Access-Control-Allow-Origin", "*");
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
MultipartFile file = multipartRequest.getFile("file");
String originalFilename = file.getOriginalFilename();
String fullPath = "";
InputStream inputStream = null;
try {
inputStream = file.getInputStream();
int index = originalFilename.lastIndexOf(".");
String fileExtName = originalFilename.substring(index + 1);
StorePath storePath = fastFileStorageClient.uploadFile(inputStream, file.getSize(), fileExtName, null);
fullPath = storePath.getFullPath();
} catch (Exception e) {
e.printStackTrace();
}
if (StringUtils.isNotEmpty(fullPath)) {
//保存文件信息
FileInfo fileInf = new FileInfo();
fileInf.setFileName(originalFilename);
fileInf.setFullPath(fullPath);
fileInfoRepository.saveAndFlush(fileInf);
// 这里开启一个线程去处理,因为时间较长
convertPdfAndImage(fullPath);
}
return fullPath;
}
3、转为图片格式的pdf文件
private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
10,
200,
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000), runnable -> new Thread(runnable, "DocumentManagementController, threadPoolExecutor-" + runnable.hashCode()));
public void convertPdfAndImage(@RequestParam(value = "fileId") String fileId) {
threadPoolExecutor.execute(() -> {
FileInfo fileInfo = fileInfoRepository.findByFullPath(fileId);
if (fileInfo == null) {
return ;
}
String fileName = fileInfo.getFileName();
String pdfFullPath = fileInfo.getPdfFullPath();
if (StringUtils.isEmpty(pdfFullPath)) {
pdfFullPath = getPdfPath(fileId, fileName);
}
fileInfo.setPdfFullPath(pdfFullPath);
// 转图片
String pdfImgPath = fileInfo.getPdfImgPath();
int imgWidth = 0;
int imgHeight = 0;
if (StringUtils.isNotEmpty(pdfFullPath) && StringUtils.isEmpty(pdfImgPath)) {
// 这里做一下文件格式的限制
if (fileName.endsWith(".pdf") || fileName.endsWith(".doc") || fileName.endsWith(".docx")) {
fileName = fileName.substring(0, fileName.lastIndexOf(".")) + ".pdf";
StorePath storePath = StorePath.parseFromUrl(pdfFullPath);
// 获取到文件流
byte[] bytes = fastFileStorageClient.downloadFile(storePath.getGroup(), storePath.getPath(), new DownloadByteArray());
// 转为图片,并且传回图片的长和高,来设置水印的位置
PdfImageVo pi = pdfService.convertImages(fileName, bytes);
// 把图片存到一个新的pdf文件中
String pdfPath = pdfService.imagesToPdf(fileName, pi);
pdfImgPath = pdfService.saveToFastDfs(pdfPath);
imgWidth = pi.getWidth();
imgHeight = pi.getHeight();
}
}
fileInfo.setPdfImgPath(pdfImgPath);
fileInfo.setImgWidth(imgWidth);
fileInfo.setImgHeight(imgHeight);
fileInfoRepository.saveAndFlush(fileInfo);
});
}
3.1 转为普通的pdf文件
@Override
public String getPdfPath(String storeFullPath, String fileName){
String ty = "pdf";
if(fileName != null && fileName.toLowerCase().endsWith(".pdf")){
return storeFullPath;
}
InputStream sbs = null;
InputStream stream = null;
try {
StorePath storePath = StorePath.parseFromUrl(storeFullPath);
byte[] bytes = fastFileStorageClient.downloadFile(storePath.getGroup(), storePath.getPath(), new DownloadByteArray());
sbs = AsposePDF.getPDFStream(bytes, fileName);
if (sbs != null) {
stream = new ByteArrayInputStream(IOUtils.toByteArray(sbs));
StorePath storePath1 = fastFileStorageClient.uploadFile(stream, stream.available(), ty, null);
//fastdfs返回的路径
String fileId = storePath1.getFullPath();
return fileId;
} else {
log.info("该文件格式不能转为PDF....");
}
} catch (Exception e) {
log.error("转为pdf文件异常。。。");
e.printStackTrace();
} finally {
if(sbs != null){
try {
sbs.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(stream != null){
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return "";
}
3.2 AsposePDF
package cn.com.bmsmart.common.util;
import com.aspose.cells.SaveFormat;
import com.aspose.cells.Workbook;
import com.aspose.slides.Presentation;
import com.aspose.words.Document;
import com.aspose.words.DocumentBuilder;
import com.lowagie.text.Image;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.PdfWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.imageio.ImageIO;
import java.io.*;
public class AsposePDF {
private static final Logger LOG = LoggerFactory.getLogger(AsposePDF.class);
public static InputStream getPDFStream(byte[] docStream, String fileName) throws Exception {
InputStream sbs = new ByteArrayInputStream(docStream);
String fileType = fileName.substring(fileName.lastIndexOf("."), fileName.length()).toLowerCase();
if (".doc".equals(fileType) || ".docx".equals(fileType) || ".rtf".equals(fileType) || ".RTF".equals(fileType)) {// 如果文件为word文件
// 则先要处理为PDF文件
ByteArrayOutputStream out = new ByteArrayOutputStream();
Document doc = new Document(sbs);
doc.save(out, 40);
ByteArrayInputStream swapStream = new ByteArrayInputStream(out.toByteArray());
out.close();
return swapStream;
} else if (".pdf".equals(fileType) || ".PDF".equals(fileType)) {
return sbs;
} else if (".xlsx".equals(fileType) || ".xls".equals(fileType)) {
// if (!getLicense("xlsxlicense.xml")) {return null;}
Workbook excel = new Workbook(sbs);
ByteArrayOutputStream out = new ByteArrayOutputStream();
excel.save(out, SaveFormat.PDF);
ByteArrayInputStream swapStream = new ByteArrayInputStream(out.toByteArray());
out.close();
return swapStream;
} else if (".ppt".equals(fileType) || ".pptx".equals(fileType)) {
// if (!getLicense("pptTopdflicense.xml")) {return null;}
Presentation pres = new Presentation(sbs);
ByteArrayOutputStream out = new ByteArrayOutputStream();
pres.save(out, com.aspose.slides.SaveFormat.Pdf);
ByteArrayInputStream swapStream = new ByteArrayInputStream(out.toByteArray());
out.close();
return swapStream;
/*--------1020 bug 2885 start--------*/
// tif不支持预览,不做为转pdf
} else if (".png".equals(fileType) || ".jpg".equals(fileType) || ".jpeg".equals(fileType)) {
/*--------1020 bug 2885 end--------*/
int lens = docStream.length;
Float f = lens / 1024f / 1024f;
System.out.println(f);
Image image = null;
if (f > 10 && !".tif".equals(fileType)) {
// 如果图片大于10M进行压缩
java.awt.Image srcFile = ImageIO.read(sbs);
int w = srcFile.getWidth(null);
ByteArrayOutputStream outs = new ByteArrayOutputStream();
String name = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length());
ImageResizeKit.resize(1000, docStream, outs, w / 2, name, 0.5f);
image = Image.getInstance(outs.toByteArray());
outs.close();
} else {
ByteArrayOutputStream baos = new ByteArrayOutputStream(2048 * 3);
for (int len; (len = sbs.read()) != -1;) {
baos.write(len);
}
baos.flush();
image = Image.getInstance(baos.toByteArray());
baos.close();
}
float width = image.getWidth();
float height = image.getHeight();
image.setAbsolutePosition(0.0f, 0.0f);
com.lowagie.text.Document document = new com.lowagie.text.Document(new Rectangle(width, height));
ByteArrayOutputStream baout = new ByteArrayOutputStream();
PdfWriter pdfWr = PdfWriter.getInstance(document, baout);
document.open();
document.add(image);
document.newPage();
// 5:释放资源
document.close();
pdfWr.close();
ByteArrayInputStream swapStream = new ByteArrayInputStream(baout.toByteArray());
baout.close();
return swapStream;
} else if (".txt".equals(fileType) || ".TXT".equals(fileType)) {
InputStream sb = new ByteArrayInputStream(docStream);
String ssd = EncodeUtils.getEncode(sb,true);
InputStreamReader isr = null;
if ("GBK".equals(ssd)) {
isr = new InputStreamReader(sbs, "GBK");
} else if ("UTF-8".equals(ssd)) {
isr = new InputStreamReader(sbs, "UTF-8");
} else {
isr = new InputStreamReader(sbs, ssd);
}
BufferedReader br = new BufferedReader(isr);
StringBuilder result = new StringBuilder();
String s = null;
while ((s = br.readLine()) != null) {// 使用readLine方法,一次读一行
result.append(System.lineSeparator() + s);
}
if (!(result.length() > 0)) {
result.append("");
}
Document doc = new Document();
DocumentBuilder builder = new DocumentBuilder(doc);
builder.writeln(result.toString());
ByteArrayOutputStream out = new ByteArrayOutputStream();
doc.save(out, 40);
ByteArrayInputStream swapStream = new ByteArrayInputStream(out.toByteArray());
out.close();
return swapStream;
}
return null;
}
}
4、下载
@GetMapping("/downloadPdf")
public void downloadPdf(@RequestParam(value = "fileId") String fileId,
@RequestParam(value = "waterText") String waterText,
HttpServletResponse response) {
FileInfo fileInfo = fileInfoRepository.findByFullPath(fileId);
if (fileInfo == null || StringUtils.isEmpty(fileInfo.getPdfFullPath())) {
return ;
}
String fileName = fileInfo.getFileName();
String pdfImgPath = fileInfo.getPdfImgPath();
String pdfFileId = fileInfo.getPdfFullPath();
if (fileName.endsWith(".pdf") || fileName.endsWith(".doc") || fileName.endsWith(".docx")) {
if (StringUtils.isEmpty(pdfFileId)) {
pdfFileId = docManagementService.getPdfPath(fileId, fileName);
fileInfo.setPdfFullPath(pdfFileId);
fileInfoRepository.save(fileInfo);
}
PdfImageVo pi = new PdfImageVo();
if (StringUtils.isEmpty(pdfImgPath)) {
fileName = fileName.substring(0, fileName.lastIndexOf(".")) + ".pdf";
StorePath storePath = StorePath.parseFromUrl(pdfFileId);
byte[] bytes = fastFileStorageClient.downloadFile(storePath.getGroup(), storePath.getPath(), new DownloadByteArray());
pi = pdfService.convertImages(fileName, bytes);
String pdfPath = pdfService.imagesToPdf(fileName, pi);
pdfImgPath = pdfService.saveToFastDfs(pdfPath);
fileInfo.setPdfImgPath(pdfImgPath);
fileInfo.setImgWidth(pi.getWidth());
fileInfo.setImgHeight(pi.getHeight());
fileInfoRepository.save(fileInfo);
}
pi.setWidth(fileInfo.getImgWidth());
pi.setHeight(fileInfo.getImgHeight());
StorePath storePath = StorePath.parseFromUrl(pdfImgPath);
byte[] bytes = fastFileStorageClient.downloadFile(storePath.getGroup(), storePath.getPath(), new DownloadByteArray());
// 加水印
String filePath = pdfService.addWaterMark(fileName, bytes, waterText, pi);
pdfService.downLoadUserTemp(fileName, filePath, response);
}
}
5、其他用到的类
public class PdfImageVo {
private int width;
private int height;
private String imageDir;
}
public class FileInfo{
@Id
private String id;
@Column(name = "file_name")
private String fileName; // 文件名称
@Column(name = "full_path")
private String fullPath; // 原文件地址
@Column(name = "pdf_full_path")
private String pdfFullPath; // 正常的pdf文件
@Column(name = "pdf_image_path")
private String pdfImgPath; // 不可操作的图片格式PDF文件
@Column(name = "image_width")
private Integer imgWidth; // 图片宽
@Column(name = "image_height")
private Integer imgHeight; // 图片高
}
package cn.com.bmsmart.service;
import cn.com.bmsmart.vo.PdfImageVo;
import javax.servlet.http.HttpServletResponse;
/**
* <p>Description: pdf 文件操作 </p>
*
* @author liangZQ
* Created by liangZQ on 2022/5/6 16:37.
*/
public interface PdfService {
/**
* 加水印
* @param fileName 文件名称
* @param bytes 文件流
* @param waterText 水印文字
* @param pi 图片属性
* @return pdf副本地址
*/
String addWaterMark(String fileName, byte[] bytes, String waterText, PdfImageVo pi);
/**
* 下载文件
*/
void downLoadUserTemp(String fileName, String filePath, HttpServletResponse response);
/**
* 把pdf文件转为images, 返回图片路径
*/
PdfImageVo convertImages(String fileName, byte[] bytes);
/**
* 图片集合转为PDF文件
*/
String imagesToPdf(String fileName, PdfImageVo pi);
/**
* pdf文件存到fastDfs
*/
String saveToFastDfs(String filePath);
}
package cn.com.bmsmart.service.impl;
import cn.com.bmsmart.service.PdfService;
import cn.com.bmsmart.vo.PdfImageVo;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Document;
import com.itextpdf.text.Element;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfGState;
import com.itextpdf.text.pdf.PdfImage;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.PdfWriter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.UUID;
/**
* <p>Description: 生成一个加水印的pdf文件,切分成图片; 把图片聚合成pdf文件,加水印,下载 </p>
*
* @author liangZQ
* Created by liangZQ on 2022/5/7 17:24.
*/
@Slf4j
@Service
public class PdfServiceImpl implements PdfService {
@Autowired
private FastFileStorageClient fastFileStorageClient;
/**
* pdf文件添加水印
*/
@Override
public String addWaterMark(String fileName, byte[] bytes, String waterText, PdfImageVo pi) {
int height = pi.getHeight();
int width = pi.getWidth();
// 临时文件目录
String dirPath = getDirPath("pdf");
// 临时文件
String newFilePath = dirPath + File.separator + System.currentTimeMillis() + "_" + fileName;
File newFile = new File(newFilePath);
try {
if (!newFile.exists()) {
newFile.createNewFile();
}
// 读pdf文件
PdfReader reader = new PdfReader(bytes);
int totalPage = reader.getNumberOfPages();
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(newFilePath));
// 设置字体
BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
// 设置透明度
PdfGState gs = new PdfGState();
gs.setFillOpacity(0.3f);
gs.setStrokeOpacity(0.3f);
for (int i = 1; i <= totalPage; i++) {
PdfContentByte content = stamper.getOverContent(i); // 内容上层
content.beginText();
content.setGState(gs); // 字体添加透明度
// 添加字体大小等
content.setColorFill(BaseColor.LIGHT_GRAY);
content.setFontAndSize(baseFont, 40);
// 具体位置 内容 旋转多少度
content.showTextAligned(Element.ALIGN_MIDDLE, waterText, (float)width/3, (float)height/6, 30);
content.showTextAligned(Element.ALIGN_MIDDLE, waterText, (float)width/3, (float)height*4/6, 30);
content.endText();
}
stamper.close();
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
return newFilePath;
}
/**
* 下载文件
*/
@Override
public void downLoadUserTemp(String fileName, String filePath, HttpServletResponse response) {
File file = new File(filePath);
if (!file.exists()) {
log.error("文件不存在:{}", filePath);
}
InputStream in = null;
OutputStream out = null;
try {
in = new FileInputStream(filePath);
byte[] bytes = FileCopyUtils.copyToByteArray(in);
response.reset();
response.setCharacterEncoding("UTF-8");
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
response.addHeader("Content-Length", bytes.length + "");
response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + URLEncoder.encode(fileName, "UTF-8"));
out = new BufferedOutputStream(response.getOutputStream());
out.write(bytes, 0, bytes.length);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
file.delete();
}
}
/**
* 把pdf文件转为images, 返回图片路径
*/
@Override
public PdfImageVo convertImages(String fileName, byte[] bytes) {
String name;
if (fileName.lastIndexOf(".") != -1) {
name = fileName.substring(0, fileName.lastIndexOf("."));
} else {
name = fileName;
}
int width = 0;
int height = 0;
String uuid = getUuid();
String imgDirPath = getDirPath("image" + File.separator + uuid);
try {
File dir = new File(imgDirPath);
if (!dir.exists()) {
dir.mkdirs();
}
// 读pdf文件
PDDocument pdDocument = PDDocument.load(bytes);
int totalPage = pdDocument.getNumberOfPages();
PDFRenderer pdfRenderer = new PDFRenderer(pdDocument);
for (int i = 0; i < totalPage; i++) {
String imgPath = imgDirPath + File.separator + name + (i + 1) + ".png";
BufferedImage image = pdfRenderer.renderImageWithDPI(i, 150);
if (i == 0) {
width = image.getWidth(null);
height = image.getHeight(null);
}
File imgFile = new File(imgPath);
ImageIO.write(image, "png", imgFile);
}
} catch (Exception e) {
e.printStackTrace();
}
PdfImageVo vo = new PdfImageVo();
vo.setImageDir(imgDirPath);
vo.setWidth(width);
vo.setHeight(height);
return vo;
}
/**
* 图片集合转为PDF文件
*/
@Override
public String imagesToPdf(String fileName, PdfImageVo pi) {
String name;
if (fileName.lastIndexOf(".") != -1) {
name = fileName.substring(0, fileName.lastIndexOf("."));
} else {
name = fileName;
}
String pdfPath = getDirPath("pdf") + File.separator + name + System.currentTimeMillis() + ".pdf";
// 判断图片集合存在
String imgDirPath = pi.getImageDir();
File imgDir = new File(imgDirPath);
if (!imgDir.exists()) {
return "";
}
File[] files = imgDir.listFiles();
if (files == null || files.length < 1) {
return "";
}
int fileLength = files.length;
try {
// 生成pdf文件
File pdf = new File(pdfPath);
if (pdf.exists()) {
pdf.delete();
}
pdf.createNewFile();
Document document = new Document();
document.setMargins(0, 0, 0, 0);
PdfWriter.getInstance(document, new FileOutputStream(pdf));
document.open();
for (int i = 0; i < fileLength; i++) {
String imagePath = imgDirPath + File.separator + name + (i + 1) + ".png";
Image image = Image.getInstance(imagePath);
image.setAlignment(Image.ALIGN_CENTER);
document.setPageSize(new Rectangle(image.getWidth(), image.getHeight()));
document.newPage();
document.add(image);
}
document.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
for (int i = 0; i < fileLength; i++) {
String imagePath = imgDirPath + File.separator + name + (i + 1) + ".png";
File img = new File(imagePath);
img.delete();
}
imgDir.delete();
}
return pdfPath;
}
@Override
public String saveToFastDfs(String filePath) {
File file = new File(filePath);
InputStream in = null;
try {
long size = file.length();
in = new FileInputStream(file);
StorePath storePath = fastFileStorageClient.uploadFile(in, size, FilenameUtils.getExtension(file.getName()), null);
return storePath.getFullPath();
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
file.delete();
}
return "";
}
public static String getUuid() {
return UUID.randomUUID().toString();
}
/**
* 获取目录地址
* @param name 自定义文件夹名称
*/
public static String getDirPath(String name) {
String dirPath = System.getProperty("user.dir") + File.separator + "convert2pdf" + File.separator + name;
File dirFile = new File(dirPath);
if (!dirFile.exists()) {
dirFile.mkdirs();
}
return dirPath;
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?