附件文件增加水印
场景描述
附件上传时,文件中没有水印,下载前,加上水印。
实现思路
- 获取附件中文件的storeFileName,用于在服务中获取文件
- 调用自定义的服务,传入storeFileName参数,使用平台提供的StorageClient类下载文件
- 使用第三方工具给文件增加水印
- 返回文件流,浏览器预览或下载文件
实现步骤
1 前端js代码
1.1 从附件中获取storeFileName
附件上传后,存入数据库中的数据是一个JSON数组,代码如下。主要包括两项内容,一是存储文件名storeFileName,二是真实文件名realFileName。
[{
"storeFileName":"anoy_CA30A885715000016616595C1A40134E外部应用导入说明.pdf",
"realFileName":"外部应用导入说明.pdf"
}]
本例在表格中放附件组件和按钮组件,界面如下图所示
- 表格中的第一列
- 表格展示样式的附件组件,有预览和下载两个按钮,点击预览按钮触发附件组件的点击预览事件,点击下载按钮触发附件组件的下载事件
- 表格中的第二列
- 默认展示样式的附件组件,点击文件名,触发附件组件的点击预览事件
- 表格中的第三列
- 使用附件以外的按钮,实现预览和下载
1.2 调用自定义服务下载或预览文件
在附件组件的点击预览事件中,给event.data.file._previewUrl赋值为自定义服务的url,从而替代了附件组件默认的预览url,代码如下:
onUpload0Preview(event){
let storeFileName = event.data.file.storeFileName;
let realFileName = event.data.file.realFileName;
let url = this.getServiceUrl("/main/fuwu/addwatermark?storeFileName="
+storeFileName+"&realFileName="+realFileName+"&operateType=preview");
event.data.file._previewUrl=url;
}
在附件组件的点击下载事件中,给event.data.file.downloadUrl赋值为自定义服务的url,从而替代了附件组件默认的下载url,代码如下:
onUpload0Download(event){
let storeFileName = event.data.file.storeFileName;
let realFileName = event.data.file.realFileName;
let url = this.getServiceUrl("/main/fuwu/addwatermark?storeFileName="
+storeFileName+"&realFileName="+realFileName+"&operateType=download");
event.data.file.downloadUrl=url;
}
在按钮的点击事件中,获取表格当前行数据中的附件列数据,取出storeFileName和realFileName,在window.open中调用自定义服务的url,实现文件预览,代码如下:
onBtnPreviewClick(event){
let files = event.context.$item.ffiles;
files = JSON.parse(files);
if(files && files.length>0){
let storeFileName = files[0].storeFileName;
let realFileName = files[0].realFileName;
let url = this.getServiceUrl("/main/fuwu/addwatermark?storeFileName="
+storeFileName+"&realFileName="+realFileName+"&operateType=preview");
window.open(url);
}
}
在按钮的点击事件中,获取表格当前行数据中的附件列数据,取出storeFileName和realFileName,在window.open中调用自定义服务的url,实现文件下载,代码如下:
onBtnDownloaadClick(event){
let files = event.context.$item.ffiles;
files = JSON.parse(files);
if(files && files.length>0){
let storeFileName = files[0].storeFileName;
let realFileName = files[0].realFileName;
let url = this.getServiceUrl("/main/fuwu/addwatermark?storeFileName="
+storeFileName+"&realFileName="+realFileName+"&operateType=download");
window.open(url);
}
}
2 后端java代码
自定义服务接收三个参数
- storeFileName:用于获取文件
- realFileName:用于返回文件名
- operateType:用于设置响应头,是预览还是下载
另外,这个服务需要返回文件流,平台的服务不能返回,因此通过自定义controller实现。
2.1 添加controller
在src/main/java/main目录下增加controller目录,在该目录下增加controller文件
controller文件的代码如下:
package main.controller;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.justep.util.SpringWebUtil;
import main.service.FuwuUserService;
@RestController
public class FuwuUserController {
@Autowired
private FuwuUserService service;
@GetMapping("/main/fuwu/addwatermark")
public void addWatermark(@RequestParam String storeFileName,@RequestParam String realFileName,@RequestParam String operateType) throws Exception {
HttpServletResponse response = SpringWebUtil.getResponse();
//调用service获取加了水印的文件流
InputStream fis = service.addWatermark(storeFileName);
try {
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();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
2.2 添加service
在src/main/java/main目录下增加service目录,在该目录下增加service文件
在service文件中添加增加水印的方法
- 用storeFileName获取文件流
- 使用第三方工具给pdf文件的第一页增加水印(代码来自于网络)
- 返回加了水印的文件流
增加水印方法的代码如下:
//增加水印
public InputStream addWatermark(String storeFileName) throws Exception {
//下载文档
StorageApi instance = StorageClient.getInstance();
InputStream is = instance.getObject(storeFileName, storeFileName, storeFileName);
PdfDocument pdf = new PdfDocument();
//加载文档
pdf.loadFromStream(is);
//获取 PDF 的第一页
PdfPageBase page = pdf.getPages().get(0);
//插入水印
insertWatermark(page, "abcd");
//保存文档
ByteArrayOutputStream os = new ByteArrayOutputStream();
pdf.saveToStream(os);
byte[] buffer = os.toByteArray();
InputStream sbs = new ByteArrayInputStream(buffer);
pdf.close();
//返回已加水印的文件流
return sbs;
}
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);
}
service文件引用的包如下:
package main.service;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.geom.Dimension2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import org.springframework.stereotype.Service;
import com.justep.clients.StorageClient;
import com.justep.clients.api.StorageApi;
import com.spire.pdf.PdfDocument;
import com.spire.pdf.PdfPageBase;
import com.spire.pdf.graphics.PdfBrushes;
import com.spire.pdf.graphics.PdfFont;
import com.spire.pdf.graphics.PdfFontFamily;
import com.spire.pdf.graphics.PdfStringFormat;
import com.spire.pdf.graphics.PdfTextAlignment;
import com.spire.pdf.graphics.PdfTilingBrush;
import com.spire.pdf.graphics.PdfTrueTypeFont;
2.3 将第三方jar复制到tomcat/ROOT下
本例中使用的spire.pdf-3.4.2.jar放到tomcat/ROOT目录下才能正常使用,model\service\main\lib目录下的jar保留,用于服务编译。
在本地ide中,将jar复制到newdao-ide-windows\eclipse-workspace-template\ROOT\WEB-INF\lib目录下即可。
在云ide中
- 将jar复制到model\service\main\deploy目录下
- 在model\service\main\deploy目录下新建install.sh文件,内容如下,含义是将当前目录下的jar复制到$JUSTEP_HOME/../tomcat/webapps-system/ROOT/WEB-INF/lib目录下
- 用system登录控制台,进入资源管理-池管理-开发池,找到当前ide的池,进入详情,进入tomcat终端,执行/usr/local/x5/model/service/main/deploy/install.sh,将jar复制到/usr/local/tomcat/webapps-system/ROOT/WEB-INF/lib目录下
- Tomcat下面有jar就可以运行pdf加水印
- install.sh的作用是发布后,应用安装或更新时会执行,可将jar复制到tomcat目录下
install.sh文件内容如下
#!/bin/bash
startDir=$(dirname $(readlink -f "$0"))
cd $startDir
cp spire.pdf-3.4.2.jar $JUSTEP_HOME/../tomcat/webapps-system/ROOT/WEB-INF/lib
model\service\main\deploy目录下的内容如下图所示
2.4 关于字体
平台提供了六种字体,分别是微软雅黑、宋体、新宋体、仿宋、黑体和楷体。字体文件放在/usr/share/fonts/chinese/TrueType目录下。
如需上传新字体,使用2.3里面介绍的,在sh文件中复制文件cp的方法,将字体文件复制到/usr/share/fonts/chinese/TrueType目录下即可。