需求:点击下载---》弹窗加入水印(可加可不加,加上就有水印,没有不加水印),点击之后下载文件
处理思路及流程
前端:
用户点击下载 → 收集水印参数 → 调用后端API → 处理响应为Blob → 触发文件下载
后端:
接收请求 → 验证权限 → 从Minio获取文件 → 判断文件类型 → 添加水印 → 返回处理后的文件流
前端
Java
加jar包
org.apache.pdfbox
pdfbox
2.0.24
import io.minio.MinioClient;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
@RestController
public class FileDownloadController {
@Autowired
private MinioClient minioClient;
@GetMapping("/api/download")
public byte[] downloadFileWithWatermark(@RequestParam String fileUrl, @RequestParam String watermarkText) throws Exception {
// 解析 MinIO 存储桶和对象名称
String[] parts = fileUrl.split("/");
String bucketName = parts[parts.length - 2];
String objectName = parts[parts.length - 1];
// 获取字节
InputStream inputStream = minioClient.getObject(GetObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build());
byte[] content = inputStreamToByteArray(inputStream);
inputStream.close();
byte[] fileBytes;
if (objectName.toLowerCase().endsWith(".pdf")) {
fileBytes = addWatermarkToPdf(content, watermarkText);
} else if (objectName.toLowerCase().endsWith(".jpg") || objectName.toLowerCase().endsWith(".png")) {
fileBytes = addWatermarkToImage(content, watermarkText);
} else {
throw new IllegalArgumentException("不支持的文件类型");
}
// 设置响应头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", URLEncoder.encode(objectName, StandardCharsets.UTF_8));
return fileBytes
}
private byte[] inputStreamToByteArray(InputStream inputStream) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[16384];
while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
return buffer.toByteArray();
}
private byte[] addWatermarkToPdf(byte[] content, String watermarkText) throws IOException {
try (PDDocument document = PDDocument.load(content)) {
for (PDPage page : document.getPages()) {
try (PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true, true)) {
contentStream.setFont(PDType1Font.HELVETICA_BOLD, 20);
contentStream.setNonStrokingColor(Color.LIGHT_GRAY);
contentStream.beginText();
contentStream.newLineAtOffset(100, 100);
contentStream.showText(watermarkText);
contentStream.endText();
}
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
document.save(outputStream);
return outputStream.toByteArray();
}
}
private byte[] addWatermarkToImage(byte[] content, String watermarkText) throws IOException {
java.io.ByteArrayInputStream bis = new java.io.ByteArrayInputStream(content);
BufferedImage image = ImageIO.read(bis);
Graphics2D g2d = image.createGraphics();
g2d.setColor(Color.LIGHT_GRAY);
g2d.setFont(new Font("Arial", Font.BOLD, 20));
g2d.drawString(watermarkText, 100, 100);
g2d.dispose();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageIO.write(image, "png", bos);
return bos.toByteArray();
}
}
修改pdf加水印,因为没有对应的字体没输入文字水印之后pdf文件会打不开
所以现在改进方法:
从网站下载对应字体的ttf,我是从c:/windows/fonts/ 下找到对应的 “simhei.ttf”文件复制在“ssrc/main/resources/fonts
”文件夹下;
修改对应加水印的代码,如下:
private byte[] addWatermarkToPdf(byte[] content, String watermarkText) throws IOException {
try (PDDocument document = PDDocument.load(content)) {
// 使用try-with-resources确保字体流关闭
try (InputStream fontStream = getClass().getResourceAsStream("/fonts/simhei.ttf")) {
//PDTrueTypeFont font = PDTrueTypeFont.loadTTF(document, fontStream);注意这是个大坑!
PDTType0Font font = PDTType0Font.load(document, fontStream);
for (PDPage page : document.getPages()) {
try (PDPageContentStream contentStream = new PDPageContentStream(
document, page, PDPageContentStream.AppendMode.APPEND, true, true)) {
contentStream.setFont(font, 20);
contentStream.setNonStrokingColor(Color.LIGHT_GRAY);
contentStream.beginText();
contentStream.newLineAtOffset(100, 100);
contentStream.showText(watermarkText);
contentStream.endText();
}
}
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
document.save(outputStream);
return outputStream.toByteArray();
}
}
这样就基本都可以出来了,样式什么的需要后续自己调节。