附件文件增加水印
运行效果
附件上传时,文件中没有水印,下载前,加上水印。
实现方法
- 获取附件中文件的 storeFileName,用于在服务中获取文件
- 调用自定义的服务,传入 storeFileName 参数,使用平台提供的 StorageClient 类下载文件
- 使用第三方工具给文件增加水印
- 返回文件流,浏览器预览或下载文件
后端开发
添加本地 pdf 水印依赖 jar 包
在服务上“设置依赖”,如下图所示
添加“本地 jar”(也可以添加 maven 配置),上传 jar 包
添加自定义服务
自定义服务 addWatermark,设置四个请求参数
- storeFileName:用于获取文件
- realFileName:用于返回文件名
- operateType:用于设置响应头,是预览还是下载
- watermark:自定义水印内容
点击“写代码”在系统自动生成的服务方法中实现增加水印并返回,代码如下:
public void addWatermark(String storeFileName,String realFileName,String operateType,String watermark) throws Exception {
HttpServletResponse response = SpringWebUtil.getResponse();
// 调用 service 获取加了水印的文件流
try(InputStream fis = this.addWatermark(storeFileName, watermark)){
response.reset();
response.setCharacterEncoding("UTF-8");
response.setContentType("application/octet-stream");
// 参数为 attachment 代表下载,参数为 inline 代表预览,预览前提是文件支持预览的,并且 contentType 不为 application/octet-stream
// 根据 operateType 参数值,确定是下载还是预览
if ("download".equals(operateType)) {
response.setHeader("Content-Disposition",
"attachment;filename=" + new String(realFileName.getBytes("utf-8"), "ISO-8859-1"));
response.setContentType("application/octet-stream");
} else {
response.setHeader("Content-Disposition",
"inline;filename=" + new String(realFileName.getBytes("utf-8"), "ISO-8859-1"));
response.setContentType("application/pdf");
}
byte[] bytes = new byte[1024];
int len;
while ((len = fis.read(bytes)) != -1) {
response.getOutputStream().write(bytes, 0, len);
}
response.flushBuffer();
fis.close();
}
}
增加水印
在 service 文件中添加增加水印的方法 addWatermark(String storeFileName, String watermark)
- 用 storeFileName 获取文件流
- 使用第三方工具给 pdf 文件的第一页增加水印(代码来自于网络)
- 返回加了水印的文件流
增加水印方法的代码如下:
//增加水印
public InputStream addWatermark(String storeFileName, String watermark) throws Exception {
// 获取文档
StorageApi instance = StorageClient.getInstance();
Response response = instance.getFileAsFeignResponse(storeFileName, storeFileName);
InputStream is=response.body().asInputStream();
PdfDocument pdf = new PdfDocument();
// 加载文档
pdf.loadFromStream(is);
PdfPageCollection pageList= pdf.getPages();
//给每一页都新增水印,最多处理5页
for(int i=0;i<pageList.getCount()&&i<5;i++) {
PdfPageBase page = pdf.getPages().get(i);
// 插入水印
insertWatermark(page, watermark);
}
// 保存文档
ByteArrayOutputStream os = new ByteArrayOutputStream();
pdf.saveToStream(os);
byte[] buffer = os.toByteArray();
InputStream sbs = new ByteArrayInputStream(buffer);
pdf.close();
// 返回已加水印的文件流
return sbs;
}
//pdf 页面新增水印
static void insertWatermark(PdfPageBase page, String watermark) {
Dimension2D dimension2D = new Dimension();
dimension2D.setSize(page.getCanvas().getClientSize().getWidth() / 2,
page.getCanvas().getClientSize().getHeight() / 3);
PdfTilingBrush brush = new PdfTilingBrush(dimension2D);
brush.getGraphics().setTransparency(0.3F);
brush.getGraphics().save();
brush.getGraphics().translateTransform((float) brush.getSize().getWidth() / 2,
(float) brush.getSize().getHeight() / 2);
brush.getGraphics().rotateTransform(-45);
PdfTrueTypeFont font1 = new PdfTrueTypeFont(new Font("宋体", Font.PLAIN, 14), true);
brush.getGraphics().drawString(watermark, font1, PdfBrushes.getViolet(), 0, 0,
new PdfStringFormat(PdfTextAlignment.Center));
brush.getGraphics().restore();
brush.getGraphics().setTransparency(1);
Rectangle2D loRect = new Rectangle2D.Float();
loRect.setFrame(new Point2D.Float(0, 0), page.getCanvas().getClientSize());
page.getCanvas().drawRectangle(brush, loRect);
}
前端开发
从附件中获取 storeFileName
附件上传后,存入数据库中的数据是一个 JSON 数组,代码如下。主要包括两项内容,一是存储文件名 storeFileName,二是真实文件名 realFileName。
[{
"storeFileName":"anoy_CA30A885715000016616595C1A40134E外部应用导入说明.pdf",
"realFileName":"外部应用导入说明.pdf"
}]
调用自定义服务下载或预览文件
在附件组件的“预览”事件中,给 event.detail.file._previewUrl 赋值为自定义服务的 url,从而替代了附件组件默认的预览 url,代码如下:
react 代码
onUpload10Preview = (event) => {
let { detail: { file } } = event;
let {storeFileName,realFileName} = file;
let url = this.getServiceUrl("/main/dataservice/addwatermark?storeFileName="
+ storeFileName + "&realFileName=" + realFileName + "&operateType=preview&watermark="+(new Date()).getTime());
//点击附件预览跳转到指定路径,可用于下载前给文件加水印
event.detail.file._previewUrl = url;
}
vue 代码
let onUpload8Preview = (event) => {
let {detail:{file}} = event;
let {storeFileName,realFileName} = file;
let url = $page.getServiceUrl("/main/file/addwatermark?storeFileName="
+ storeFileName + "&realFileName=" + realFileName + "&operateType=preview&watermark="+(new Date()).getTime());
//点击附件预览跳转到指定路径,可用于下载前给文件加水印
event.detail.file._previewUrl = url;
}
在附件组件的“下载”事件中,给 event.detail.file.downloadUrl 赋值为自定义服务的 url,从而替代了附件组件默认的下载 url,代码如下:
react 代码
onUpload10Download = (event) => {
let { detail: { file } } = event;
let {storeFileName,realFileName} = file;
let url = this.getServiceUrl("/main/dataservice/addwatermark?storeFileName="
+ storeFileName + "&realFileName=" + realFileName + "&operateType=download&watermark="+(new Date()).getTime());
//点击附件下载跳转到指定路径,可用于下载前给文件加水印
event.detail.file.downloadUrl = url;
}
vue 代码
let onUpload8Download = (event) => {
let {detail:{file}} = event;
let {storeFileName,realFileName} = file;
let url = $page.getServiceUrl("/main/file/addwatermark?storeFileName="
+ storeFileName + "&realFileName=" + realFileName + "&operateType=download&watermark="+(new Date()).getTime());
//点击附件下载跳转到指定路径,可用于下载前给文件加水印
event.detail.file.downloadUrl = url;
}
关于字体
上面代码中,添加水印使用了“宋体”,平台本身不带字体,需上传字体后才能使用,参考《增加字体》