SpringBoot+POI方式导出excel加水印

Posted 经理,天台风好大

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot+POI方式导出excel加水印相关的知识,希望对你有一定的参考价值。


本篇是针对接口开发的案例~~

《EasyExcel导出导入的方式参考链接》


一、Poi实现Excel导出

Apache POI提供了HSSFWorkbook操作2003版本的Excel文件, XSSFWorkbook操作2007版Excel文件.

Excel文件导出操作在我们工作中有很多场景都会用到. 如报表数据下载, 消费记录下载等…

下面针对不同场景, 封装了不同的工具类, 但底层都是用的基础的公共api, 我使用的是HSSFWorkbook, 如果要生成2007版的Excel文件, 只需将HSSFWorkbook替换成XSSFWorkbook即可。

简单的具体实现在网上有很多案例可以参考学习, 我就不写入门案例了, 下面我会将自己工作中用到的场景封装成工具类记录下来, 供以后翻看复习。

注意:

EXCEL的压缩率特别高,能达到80%,12M的文件压缩后才2M左右。 如果未经过压缩、不仅会占用用户带宽,且会导致负载服务器(apache)和应用服务器之间,长时间占用连接(二进制流转发),导致负载服务器请求阻塞,不能提供服务。

  • 一定要注意文件流的关闭
  • 防止前台(页面)连续触发导出EXCEL

二、工具类参考

所有方式仅供参考,具体实现根据业务自行封装对应的工具类。

2.1 maven参考

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>3.8</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.16</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>ooxml-schemas</artifactId>
    <version>1.0</version>
</dependency>

<!--hutool 工具类 -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>4.5.11</version>
</dependency>

2.2 ExcelUtils

此工具类以下几种输出方式:

  1. 直接导出excel
  2. 导出excel,带背景图水印(不可打印)
  3. 导出excel,带插入图片的水印(可打印),原理类似于打印单上带着印章
  4. 其他几种导出方式自行研究。
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.ai.sop.common.constants.WebConstant;
import com.ai.system.entity.beans.TdSysStaff;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.merge.LoopMergeStrategy;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.SimpleColumnWidthStyleStrategy;
import com.alibaba.excel.write.style.row.SimpleRowHeightStyleStrategy;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.*;
import java.awt.Color;
import java.awt.Font;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List;

/**
 * @author daniel
 * @description excel 导出工具类
 */

public class ExcelUtils 

    private static final Logger log = LoggerFactory.getLogger(ExcelUtils.class);

    /**
     * 用户信息导出类(添加水印,可打印,插入图片的方式)
     * @desc 支持HSSFWorkbook格式的导出,插入图片方法.没有考虑合并单元格等情况.
     * @param response 响应
     * @param fileName 文件名
     * @param columnList 每列的标题名
     * @param dataList 导出的数据
     */
    public static void exportExcelWaterMarkPrint(HttpServletResponse response,String fileName,List<String> columnList,List<List<String>> dataList)
        //声明输出流
        OutputStream os = null;
        //设置响应头
        setResponseHeader(response,fileName);
        try 
            //获取输出流
            os = response.getOutputStream();
            String user = "daniel";
            String date = DateUtil.format(new Date(), DatePattern.NORM_DATETIME_FORMAT); //yyyy-MM-dd HH:mm:ss
            String watermark = user + "  " + date;
//            String watermark = "daniel 2022-09-29 11:55:66";

            //内存中保留1000条数据,以免内存溢出,其余写入硬盘
            HSSFWorkbook wb = new HSSFWorkbook();
            log.info("报表导出Size: " + dataList.size() + "条。");

            Sheet sheet = null;
            int excelRow = 0;//定义表格行
            int index = 1;//定义初始sheet页码
            HSSFPatriarch patriarch = null;
            BufferedImage waterimg = null;
            java.io.ByteArrayOutputStream wos = null;
            List<HSSFClientAnchor> wanList = null;
            sheet = wb.createSheet("sheet" + index);
            sheet.setDefaultColumnWidth((short)20);//将默认的列宽设为25个文字大小
            //创建标题行
            Row titleRow = sheet.createRow(excelRow++);
            for(int i = 0;i<columnList.size();i++)
                //创建该行下的每一列,并写入标题数据
                Cell cell = titleRow.createCell(i);
                cell.setCellValue(columnList.get(i));
            
            //设置内容行
            if(dataList != null && dataList.size() > 0)
                //外层for循环创建行
                for(int i = 0;i<dataList.size();i++) 
                    Row dataRow = sheet.createRow((i+1)%65000 == 0 ? 65000 : (i+1)%65000);
                    //内层for循环创建每行对应的列,并赋值
                    for(int j = 0;j<dataList.get(i).size();j++)
                        Cell cell = dataRow.createCell(j);
                        cell.setCellValue(dataList.get(i).get(j));
                    
                    //每65000条数据创建一个sheet页
                    if ((i+1)%65000 == 0) 
                        index++;
                        sheet = wb.createSheet("sheet" + index);
                        sheet.setDefaultColumnWidth((short)20);//将默认的列宽设为20个文字大小
                        Row title = sheet.createRow(0);
                        for(int k = 0; k < columnList.size();k++)//新建的sheet页表头写入
                            //创建该行下的每一列,并写入标题数据
                            Cell cell = title.createCell(k);
                            cell.setCellValue(columnList.get(k));
                        
                    
                
            
            //获取excel工作簿中sheet个数
            int sheets = wb.getNumberOfSheets();
            wos = new java.io.ByteArrayOutputStream();
            waterimg = createWaterRemark(watermark);//设置水印图片
            ImageIO.write(waterimg, "png", wos);

            //遍历sheet页并计算水印覆盖面积
            for (int q = 0; q < sheets; q++) 
                sheet.protectSheet("123456");//excel表格的编辑密码
                sheet = wb.getSheetAt(q);//获取sheet
                //开始在每个sheet页部署水印
                patriarch = (HSSFPatriarch) sheet.createDrawingPatriarch();
                //计算水印覆盖面积
                wanList = circulationPic((HSSFSheet) sheet, watermark);
                //根据行与列计算实际所需多少水印
                for (HSSFClientAnchor wanchor : wanList) 
                    patriarch.createPicture(wanchor, wb.addPicture(wos.toByteArray(), HSSFWorkbook.PICTURE_TYPE_PNG));
                
            
            //将整理好的excel数据写入流中
            wb.write(os);
         catch (IOException e) 
            e.printStackTrace();
         finally 
            try 
                // 关闭输出流
                if (os != null) 
                    os.close();
                
             catch (IOException e) 
                e.printStackTrace();
            
        
    

    /**
     * 用户信息导出类(带水印,背景图,非插入图片的方式)
     * @param response 响应
     * @param fileName 文件名
     * @param columnList 每列的标题名
     * @param dataList 导出的数据
     */
    public static void exportExcelWaterMark(HttpServletResponse response, String fileName, List<String> columnList, List<List<String>> dataList)
        //声明输出流
        OutputStream os = null;
        //设置响应头
        setResponseHeader(response,fileName);
        try 
            //获取输出流
            os = response.getOutputStream();
            //内存中保留1000条数据,以免内存溢出,其余写入硬盘
            SXSSFWorkbook wb = new SXSSFWorkbook(1000);
            //获取该工作区的第一个sheet
            SXSSFSheet sheet1 = wb.createSheet("sheet1");
            //添加水印
            WaterMarkUtil.insertWaterMarkTextToXlsx(wb);
            sheet1.trackAllColumnsForAutoSizing();
            int excelRow = 0;
            //创建标题行
            Row titleRow = sheet1.createRow(excelRow++);
            for(int i = 0;i<columnList.size();i++)
                //创建该行下的每一列,并写入标题数据
                Cell cell = titleRow.createCell(i);
                cell.setCellValue(columnList.get(i));
                sheet1.autoSizeColumn(i);
                sheet1.setColumnWidth(i,sheet1.getColumnWidth(i)*17/10);
            
            //设置内容行
            if(dataList!=null && dataList.size()>0)
                //序号是从1开始的
//                int count = 1;
                //外层for循环创建行
                for(int i = 0;i<dataList.size();i++)
                    Row dataRow = sheet1.createRow(excelRow++);
                    //内层for循环创建每行对应的列,并赋值(这种写法加了一个序号列。)
//                    for(int j = -1;j<dataList.get(0).size();j++)//由于多了一列序号列所以内层循环从-1开始
//                        Cell cell = dataRow.createCell(j+1);
//                        if(j==-1)
//                        //第一列是序号列,不是在数据库中读取的数据,因此手动递增赋值
//                            cell.setCellValue(count++);
//                        
//                        else//其余列是数据列,将数据库中读取到的数据依次赋值
//                            cell.setCellValue(dataList.get(i).get(j));
//                        
//                    
                    //根据业务重新修改(如果第一列不要序号列,为了防止第一列为空导致异常,不能get(0),要get(i))
                    for (int j = 0; j<dataList.get(i).size(); j++)
                    
                        Cell cell = dataRow.createCell(j);
                        cell.setCellValue(dataList.get(i).get(j));
                        sheet1.autoSizeColumn(j);
                        sheet1.setColumnWidth(j, sheet1.getColumnWidth(j) * 17 / 10);
                    
                
            
            //设置密码
            sheet1.protectSheet("123456");
            //将整理好的excel数据写入流中
            wb.write(os);
         catch (IOException e) 
            e.printStackTrace();
         finally 
            try 
                // 关闭输出流
                if (os != null) 
                    os.close();
                
             catch (IOException e) 
                e.printStackTrace();
            
        
    
    /**
     * 用户信息导出类(不带水印直接导出)
     * @param response 响应
     * @param fileName 文件名
     * @param columnList 每列的标题名
     * @param dataList 导出的数据
     */
    public static void exportExcel(HttpServletResponse response, String fileName, List<String> columnList, List<List<String>> dataList)
        //声明输出流
        OutputStream os = null;
        //设置响应头
        setResponseHeader(response,fileName);
        try 
            //获取输出流
            os = response.getOutputStream();
            //内存中保留1000条数据,以免内存溢出,其余写入硬盘
            SXSSFWorkbook wb = new SXSSFWorkbook(1000);
            //获取该工作区的第一个sheet
            SXSSFSheet sheet1 = wb.createSheet("sheet1");

            sheet1.trackAllColumnsForAutoSizing();
            int excelRow = 0;
            //创建标题行
            Row titleRow = sheet1.createRow(excelRow++);
            for(int

以上是关于SpringBoot+POI方式导出excel加水印的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot+POI方式导出excel加水印

springboot使用poi导出数据生成excel(.xlsx)文件

Springboot使用POI实现导出Excel文件示例

springboot 实现后端接口操作Excel的导出批量导入功能

Springboot利用poi导出excel下载

springboot结合POI导入导出excel文件