上篇 这么优秀的Excel工具类,你难道不用?介绍了Java使用poi操作excel表格的导入和修改,在日常开发中经常也会遇到在页面上点击按钮将数据库中的数据导出到excel表中;
在了解Excel的水印其实就是插入艺术字再修改字体的颜色、字体、透明度就变成了所谓的水印效果了(一顿操作后我发现其实就类似插入一张透明文字图片);
思路:
根据对Excel的了解及上网查阅了几篇文章后,整理出了思路。
在对Excel完成数据的写入后,可以生成一张类似的透明文字图片,再将该图片插入到Excel表格中,就形成了水印的效果;
来人,上代码!!!
接口请求控制层:
@RestController
@RequestMapping("v1/perFee")
public class PerFeeController {
@GetMapping("/expirApplyedList")
public void expirApplyedList(HttpServletResponse response,@RequestParam(required = false) Integer status, @RequestParam(required = false) String name,
@RequestParam(required = false) String idNumber, @RequestParam(required = false) String phone ,
@RequestParam(required = false) String exhId,@RequestParam(required = true) String filePath
) {
PerFee perFee=new PerFee();
perFee.setStatus(status);
perFee.setName(name);
perFee.setPhone(phone);
perFee.setIdNumber(idNumber);
perFee.setExhId(exhId);
List<PerFee> perFeeList=perFeeService.applyedList(perFee);
//excel标题
String[] title = {"报销编号","报销人", "实际报销金额","状态"};
//excel文件名
String fileName = "报销费用审核_" + System.currentTimeMillis() + "." + filePath;
//sheet名
String sheetName = "报销费用审核";
String [][] content = new String[perFeeList.size()][title.length];
for (int i = 0,size=perFeeList.size();i < size; i++) {
//content[i] = new Stri ng[title.length];
PerFee obj = perFeeList.get(i);
content[i][0] = String.valueOf(i+1);
content[i][1] = obj.getName();
content[i][2] = obj.getAccount();
Integer status1 = obj.getStatus();
String stat = "";
if (status1 == 1) {
stat = "财务审核中";
}else if (status1 == 2) {
stat = "已通过审核";
}
content[i][3] = stat;
}
//创建HSSFWorkbook
Workbook wb = ExcelUtils.expirWorkbook(sheetName, title, content, createWorkbook(filePath));
//响应到客户端
OutputStream os = null;
try {
try {
fileName = new String(fileName.getBytes(), "ISO8859-1");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
response.setContentType("application/octet-stream;charset=ISO8859-1");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
response.addHeader("Pargam", "no-cache");
response.addHeader("Cache-Control", "no-cache");
os = response.getOutputStream();
wb.write(os);
os.flush();
} catch (Exception ex) {
ex.printStackTrace();
}finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 根据文件格式创建Workbook
* @param filePath
* @return
*/
private static Workbook createWorkbook(String filePath) {
Workbook wb = null;
try {
//如果文件后缀名为xls
if ("xls".equals(filePath)) {
wb = new HSSFWorkbook();
}//如果文件后缀名为xlsx
else if ("xlsx".equals(filePath)) {
wb = new XSSFWorkbook();
} else {
wb = null;
}
}catch (Exception e) {
e.printStackTrace();
}
return wb;
}
}
ExcelUtils工具类:
public class ExcelUtils{
/**
* 导出Excel
* @param sheetName sheet名称
* @param title 标题(列头)
* @param values 内容
* @param wb Workbook对象 工作簿
* @return
*/
public static Workbook expirWorkbook(String sheetName, String []title, String [][]values, Workbook wb){
// 第一步,在workbook中添加一个sheet,对应Excel文件中的sheet
Sheet sheet = wb.createSheet(sheetName);
//设置列宽
sheet.setColumnWidth(2,256*19+184);
sheet.setColumnWidth(3,256*12+184);
//sheet.setColumnWidth(4,256*20+184);
//设置工作簿保护模式,只读不可修改。使用UUID获取随机值作为密码
sheet.protectSheet(UUID.randomUUID().toString());
// 第二步,在sheet中添加表头第0行,注意老版本poi对Excel的行数列数有限制
Row row = sheet.createRow(0);
// 第三步,创建单元格,并设置值表头 设置表头居中
CellStyle style = wb.createCellStyle();
//style.setAlignment(HSSFCellStyle.ALIGN_CENTER); // 创建一个居中格式
//声明列对象
Cell cell = null;
//创建标题
for(int i=0;i<title.length;i++){
cell = row.createCell(i);
cell.setCellValue(title[i]);
cell.setCellStyle(style);
}
//创建内容
for(int i=0;i<values.length;i++){
row = sheet.createRow(i + 1);
for(int j=0;j<values[i].length;j++){
//将内容按顺序赋给对应的列对象
row.createCell(j).setCellValue(values[i][j]);
}
}
//给Excel设置水印
painWaterMark(wb,sheet,"I Love You");
return wb;
}
/**
* 给Excel添加水印
* @param wb 工作簿
* @param sheet sheet页
* @param content 水印内容
*/
private static void painWaterMark(Workbook wb,Sheet sheet,String content) {
String imgFileName = "waterMark_photo_"+content+".png";
//创建水印图片 (默认保存到classes目录下)
createWaterMark(content,imgFileName);
//将图片写入到excel中
try {
//也可以动态获取sheet中的行和列,根据行和列适当的放置水印图片
//获取excel实际所占行
//int row = sheet.getFirstRowNum() + sheet.getLastRowNum();
//获取excel实际所占列
//int cell = sheet.getRow(sheet.getFirstRowNum()).getLastCellNum() + 1;
putWaterRemarkToExcel(wb, sheet, imgFileName, 0, 0, 5, 5,2, 10, 2, 2);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 创建水印图片
* 注意:生成的图片会默认保存到classes目录下,可以根据自己的业务进行更改
* @param content
* @param fileName
* @return
* @throws IOException
*/
private static String createWaterMark(String content, String fileName) {
//生成水印图片的宽度
Integer width = 300;
//水印图片的高度
Integer height = 150;
// 获取bufferedImage对象
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
String fontType = "宋体";
Integer fontStyle = java.awt.Font.PLAIN;
Integer fontSize = 50;
java.awt.Font font = new java.awt.Font(fontType, fontStyle, fontSize);
// 获取Graphics2d对象
Graphics2D g2d = image.createGraphics();
image = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
g2d.dispose();
g2d = image.createGraphics();
g2d.setColor(new java.awt.Color(0, 0, 0, 80)); //设置字体颜色和透明度
// 设置字体
g2d.setStroke(new BasicStroke(1));
// 设置字体类型 加粗 大小
g2d.setFont(font);
//设置倾斜度
g2d.rotate(Math.toRadians(-10), (double) image.getWidth() / 2, (double) image.getHeight() / 2);
FontRenderContext context = g2d.getFontRenderContext();
Rectangle2D bounds = font.getStringBounds(content, context);
double x = (width - bounds.getWidth()) / 2;
double y = (height - bounds.getHeight()) / 2;
double ascent = -bounds.getY();
double baseY = y + ascent;
// 写入水印文字原定高度过小,所以累计写水印,增加高度
g2d.drawString(content, (int) x, (int) baseY);
// 设置透明度
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
// 释放对象
g2d.dispose();
String targetImagePath = Thread.currentThread().getContextClassLoader().getResource("").getPath()+fileName;
try {
ImageIO.write(image, "png", new File(targetImagePath));
} catch (IOException e) {
e.printStackTrace();
}
return targetImagePath;
}
/**
* 为Excel打上水印工具函数 请自行确保参数值,以保证水印图片之间不会覆盖。在计算水印的位置的时候,并没有考虑到单元格合并的情况,请注意
* @param wb Excel Workbook
* @param sheet 需要打水印的Excel
* @param waterRemarkPath 水印地址,classPath,目前只支持png格式的图片,
* 因为非png格式的图片打到Excel上后可能会有图片变红的问题,且不容易做出透明效果。
* 同时请注意传入的地址格式,应该为类似:"\\excelTemplate\\test.png"
* @param startXCol 水印起始列
* @param startYRow 水印起始行
* @param betweenXCol 水印横向之间间隔多少列
* @param betweenYRow 水印纵向之间间隔多少行
* @param XCount 横向共有水印多少个
* @param YCount 纵向共有水印多少个
* @param waterRemarkWidth 水印图片宽度为多少列
* @param waterRemarkHeight 水印图片高度为多少行
* @throws IOException
*/
private static void putWaterRemarkToExcel(Workbook wb, Sheet sheet, String waterRemarkPath, int startXCol,
int startYRow, int betweenXCol, int betweenYRow, int XCount, int YCount, int waterRemarkWidth,
int waterRemarkHeight) throws IOException {
// 校验传入的水印图片格式
if (!waterRemarkPath.endsWith("png") && !waterRemarkPath.endsWith("PNG")) {
throw new RuntimeException("向Excel上面打印水印,目前支持png格式的图片。");
}
// 加载图片
ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
//InputStream imageIn = new FileInputStream(waterRemarkPath);
InputStream imageIn = Thread.currentThread().getContextClassLoader().getResourceAsStream(waterRemarkPath);
if (null == imageIn || imageIn.available() < 1) {
throw new RuntimeException("向Excel上面打印水印,读取水印图片失败(1)。");
}
BufferedImage bufferImg = ImageIO.read(imageIn);
if (null == bufferImg) {
throw new RuntimeException("向Excel上面打印水印,读取水印图片失败(2)。");
}
ImageIO.write(bufferImg, "png", byteArrayOut);
// 开始打水印
Drawing drawing = sheet.createDrawingPatriarch();
// 按照共需打印多少行水印进行循环
for (int yCount = 0; yCount < YCount; yCount++) {
// 按照每行需要打印多少个水印进行循环
for (int xCount = 0; xCount < XCount; xCount++) {
// 创建水印图片位置
int xIndexInteger = startXCol + (xCount * waterRemarkWidth) + (xCount * betweenXCol);
int yIndexInteger = startYRow + (yCount * waterRemarkHeight) + (yCount * betweenYRow);
/** 参数定义:第一个参数是(x轴的开始节点);第二个参数是(是y轴的开始节点);第三个参数是(是x轴的结束节点);
* 第四个参数是(是y轴的结束节点);第五个参数是(是从Excel的第几列开始插入图片,从0开始计数);
* 第六个参数是(是从excel的第几行开始插入图片,从0开始计数);第七个参数是(图片宽度,共多少列);
* 第8个参数是(图片高度,共多少行);*/
ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, xIndexInteger,
yIndexInteger, xIndexInteger + waterRemarkWidth, yIndexInteger + waterRemarkHeight);
Picture pic = drawing.createPicture(anchor, wb.addPicture(byteArrayOut.toByteArray(), Workbook.PICTURE_TYPE_PNG));
pic.resize();
}
}
}
}
以上代码亲测有效,可以直接复制下来根据业务修改部分代码进行测试。
效果:
怎么样?看起来是不是和正常水印一样的效果!ᥬ᭄