• 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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    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
    //                        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 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
    //                        Cell cell = dataRow.createCell(j+1);
    //                        if(j==-1)
    //                        {//第一列是序号列,不是在数据库中读取的数据,因此手动递增赋值
    //                            cell.setCellValue(count++);
    //                        }
    //                        else{//其余列是数据列,将数据库中读取到的数据依次赋值
    //                            cell.setCellValue(dataList.get(i).get(j));
    //                        }
    //                    }
                        //根据业务重新修改
                        for (int j = 0; j<dataList.get(0).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);
                        }
                    }
                }
                //将整理好的excel数据写入流中
                wb.write(os);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    // 关闭输出流
                    if (os != null) {
                        os.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        /**
         *  设置浏览器下载响应头
         */
        private static void setResponseHeader(HttpServletResponse response, String fileName) {
            try {
                try {
                    fileName = new String(fileName.getBytes("UTF-8"),"ISO-8859-1");
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                response.setContentType("application/octet-stream;charset=UTF-8");
    //            response.setContentType("application/vnd.ms-excel;charset=UTF-8");
                response.setHeader("Content-Disposition", "attachment;filename="+ fileName);
                response.addHeader("Pargam", "no-cache");
                response.addHeader("Cache-Control", "no-cache");
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    
        /**
         * @param entityClass excel中每一行数据的实体类
         * @param in excel文件
         * @param fields 字段名字 需要注意的是这个方法中的map中: excel表格中每一列名为键,每一列对应的实体类的英文名为值
         * @throws Exception
         */
        public static <T> List<T> ExcelToList(InputStream in, Class<T> entityClass, Map<String, String> fields) throws Exception {
    
            List<T> resultList = new ArrayList<T>();
    
            XSSFWorkbook workbook = new XSSFWorkbook(in);
    
            // excel中字段的中英文名字数组
            String[] egtitles = new String[fields.size()];
            String[] cntitles = new String[fields.size()];
            Iterator<String> it = fields.keySet().iterator();
            int count = 0;
            while (it.hasNext()) {
                String cntitle = (String) it.next();
                String egtitle = fields.get(cntitle);
                egtitles[count] = egtitle;
                cntitles[count] = cntitle;
                count++;
            }
    
            // 得到excel中sheet总数
            int sheetcount = workbook.getNumberOfSheets();
    
            if (sheetcount == 0) {
                workbook.close();
                throw new Exception("Excel文件中没有任何数据");
            }
    
            // 数据的导出
            for (int i = 0; i < sheetcount; i++) {
                Sheet sheet = workbook.getSheetAt(i);
                if (sheet == null) {
                    continue;
                }
                // 每页中的第一行为标题行,对标题行的特殊处理
                Row firstRow = sheet.getRow(0);
                int celllength = firstRow.getLastCellNum();
    
                String[] excelFieldNames = new String[celllength];
                LinkedHashMap<String, Integer> colMap = new LinkedHashMap<String, Integer>();
    
                // 获取Excel中的列名
                for (int f = 0; f < celllength; f++) {
                    Cell cell = firstRow.getCell(f);
                    excelFieldNames[f] = cell.getStringCellValue().trim();
                    // 将列名和列号放入Map中,这样通过列名就可以拿到列号
                    for (int g = 0; g < excelFieldNames.length; g++) {
                        colMap.put(excelFieldNames[g], g);
                    }
                }
                // 由于数组是根据长度创建的,所以值是空值,这里对列名map做了去空键的处理
                colMap.remove(null);
                // 判断需要的字段在Excel中是否都存在
                // 需要注意的是这个方法中的map中:中文名为键,英文名为值
                boolean isExist = true;
                List<String> excelFieldList = Arrays.asList(excelFieldNames);
                for (String cnName : fields.keySet()) {
                    if (!excelFieldList.contains(cnName)) {
                        isExist = false;
                        break;
                    }
                }
                // 如果有列名不存在,则抛出异常,提示错误
                if (!isExist) {
                    workbook.close();
                    throw new Exception("Excel中缺少必要的字段,或字段名称有误");
                }
                String endName = "default";
                // 将sheet转换为list
                for (int j = 1; j <= sheet.getLastRowNum(); j++) {
                    boolean flag = false;
                    String enNormalName = "";
                    String content = "";
                    Row row = sheet.getRow(j);
                    // 根据泛型创建实体类
                    T entity = entityClass.newInstance();
                    // 给对象中的字段赋值
                    for (Map.Entry<String, String> entry : fields.entrySet()) {
                        // 获取中文字段名
                        String cnNormalName = entry.getKey();
                        // 获取英文字段名
                        enNormalName = entry.getValue();
                        // 根据中文字段名获取列号
                        int col = colMap.get(cnNormalName);
                        // 获取当前单元格中的内容
                        content = "";
                        if (row.getCell(col) != null){
                            if(StringUtils.isNotBlank(row.getCell(col).toString())){
                                flag = true;
                            }
                            content = row.getCell(col).toString().trim();
                        }
                        if(enNormalName.equals("name") &&  content.equals("")){
                            endName = "";
                            break;
                        }
                        // 给对象赋值
                        setFieldValueByName(enNormalName, content, entity);
                    }
                    if(!endName.equals("") && flag){
                        resultList.add(entity);
                    }
                }
            }
            workbook.close();
            return resultList;
        }
    
        /**
         * @MethodName : setFieldValueByName
         * @Description : 根据字段名给对象的字段赋值
         * @param fieldName
         *            字段名
         * @param fieldValue
         *            字段值
         * @param o
         *            对象
         */
        private static void setFieldValueByName(String fieldName, Object fieldValue, Object o) throws Exception {
    
            Field field = getFieldByName(fieldName, o.getClass());
            if (field != null) {
                field.setAccessible(true);
                // 获取字段类型
                Class<?> fieldType = field.getType();
    
                // 根据字段类型给字段赋值
                if (String.class == fieldType) {
                    field.set(o, fieldValue);
                } else if ((Integer.TYPE == fieldType) || (Integer.class == fieldType)) {
                    if(!fieldValue.equals("")){
                        field.set(o, Integer.parseInt(fieldValue.toString()));
                    }
                }
    //            else if ((Long.TYPE == fieldType) || (Long.class == fieldType)) {
    //                field.set(o, Long.valueOf(fieldValue.toString()));
    //            }
                else if ((Float.TYPE == fieldType) || (Float.class == fieldType)) {
                    field.set(o, Float.valueOf(fieldValue.toString()));
                } else if ((Short.TYPE == fieldType) || (Short.class == fieldType)) {
                    field.set(o, Short.valueOf(fieldValue.toString()));
                } else if ((Double.TYPE == fieldType) || (Double.class == fieldType)) {
                    field.set(o, Double.valueOf(fieldValue.toString()));
                } else if (Character.TYPE == fieldType) {
                    if ((fieldValue != null) && (fieldValue.toString().length() > 0)) {
                        field.set(o, Character.valueOf(fieldValue.toString().charAt(0)));
                    }
                } else if (Date.class == fieldType) {
                    field.set(o, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(fieldValue.toString()));
                } else {
                    field.set(o, fieldValue);
                }
            } else {
                throw new Exception(o.getClass().getSimpleName() + "类不存在字段名 " + fieldName);
            }
        }
    
        /**
         * @MethodName : getFieldByName
         * @Description : 根据字段名获取字段
         * @param fieldName
         *            字段名
         * @param clazz
         *            包含该字段的类
         * @return 字段
         */
        private static Field getFieldByName(String fieldName, Class<?> clazz) {
            // 拿到本类的所有字段
            Field[] selfFields = clazz.getDeclaredFields();
            // 如果本类中存在该字段,则返回
            for (Field field : selfFields) {
                if (field.getName().equals(fieldName)) {
                    return field;
                }
            }
            // 否则,查看父类中是否存在此字段,如果有则返回
            Class<?> superClazz = clazz.getSuperclass();
            if (superClazz != null && superClazz != Object.class) {
                return getFieldByName(fieldName, superClazz);
            }
            // 如果本类和父类都没有,则返回空
            return null;
        }
    
        public static void exportNewExcel(HttpServletResponse response, String fileName, List<String> columnList, List<List<String>> dataList,String teamType) throws IOException {
            setResponseHeader(response,fileName);
            WriteCellStyle headWriteCellStyle = new WriteCellStyle();
            //设置头居中
            headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
            //内容策略
            WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
            //设置 水平居中
            contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);
            HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            //response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(2, 1);
            if ("B22".equals(teamType)){
                EasyExcel.write(response.getOutputStream(), HYEXCELData.class).autoCloseStream(Boolean.FALSE).registerWriteHandler(horizontalCellStyleStrategy).sheet(fileName).
                        registerWriteHandler(new SimpleColumnWidthStyleStrategy(25)) .registerWriteHandler(new SimpleRowHeightStyleStrategy((short)25,(short)25)).
                        doWrite(dataList);
            }
            if ("B30".equals(teamType)){
                EasyExcel.write(response.getOutputStream(), SQEXCELData.class).autoCloseStream(Boolean.FALSE).registerWriteHandler(horizontalCellStyleStrategy).sheet(fileName).
                        registerWriteHandler(new SimpleColumnWidthStyleStrategy(25)) .registerWriteHandler(new SimpleRowHeightStyleStrategy((short)25,(short)25)).
                        doWrite(dataList);
            }
    
    
        }
    
        public static void exportNoNumberExcel(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
                Sheet sheet1 = wb.createSheet("sheet1");
                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));
                }
                //设置内容行
                if(dataList!=null && dataList.size()>0){
                    //外层for循环创建行
                    for(int i = 0;i<dataList.size();i++){
                        Row dataRow = sheet1.createRow(excelRow++);
                        //内层for循环创建每行对应的列,并赋值
                        for(int j = 0;j<dataList.get(0).size();j++){
                            Cell cell = dataRow.createCell(j);
                            cell.setCellValue(dataList.get(i).get(j));
                        }
                    }
                }
                //将整理好的excel数据写入流中
                wb.write(os);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    // 关闭输出流
                    if (os != null) {
                        os.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        /**
         * 生成水印图片
         */
        public static BufferedImage createWaterRemark(String watermark) {
            //设置字体格式
            Font font = new Font("宋体", Font.PLAIN, 20);
            int width = 300;
            int height = 200;
            //宽度
            BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);// 获取bufferedImage对象
            // 背景透明 开始
            Graphics2D loGraphic = img.createGraphics();// 获取Graphics2d对象
            img = loGraphic.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
            loGraphic.dispose();
            // 背景透明 结束
            loGraphic = img.createGraphics();
            loGraphic.setColor(Color.gray);//设置画笔颜色
            loGraphic.setStroke(new BasicStroke(1));//设置字体
            loGraphic.setFont(font);//设置画笔字体
            loGraphic.rotate(Math.toRadians(-30), (double) img.getWidth() / 2, (double) img.getHeight() / 2);//设置水印倾斜度
            // 设置透明度
            loGraphic.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
            //设置字体平滑
            loGraphic.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    
            FontRenderContext context = loGraphic.getFontRenderContext();
            Rectangle2D bounds = font.getStringBounds(watermark, context);
            double x = (width - bounds.getWidth()) / 2;
            double y = (height - bounds.getHeight()) / 2;
            double ascent = -bounds.getY();
            double baseY = y + ascent;
            // 写入水印文字原定高度过小,所以累计写水印,增加高度
            loGraphic.drawString(watermark, (int) x, (int) baseY);
    
            //释放画笔
            loGraphic.dispose();
            return img;
        }
    
        /**
         * 计算应被水印覆盖的面积
         */
        public static List<HSSFClientAnchor> circulationPic(HSSFSheet sheet, String watermark) {
            List<HSSFClientAnchor> list = new ArrayList<>();
            int maxColum = 0;//列
            int lastrow = sheet.getLastRowNum();//获取最后一行
            for (int i = 0; i < lastrow; i++) {
                int rows = sheet.getRow(i).getPhysicalNumberOfCells();
                if (rows - maxColum > 0) {
                    maxColum = sheet.getRow(i).getLastCellNum();
                }
            }
            //单元格宽和高
            double cellwidth = sheet.getColumnWidthInPixels(0) * 1.0;
            double cellheight = 22.0;
    
            int columLimit = (int) Math.ceil(watermark.length() * 10 / cellwidth);
            int rowLimit = (int) Math.ceil(watermark.length() * 10 / 2.0 / cellheight);
    
            maxColum = (maxColum > columLimit && maxColum % columLimit == 0) ? maxColum : (int) Math.ceil(maxColum / columLimit * 1.0) * columLimit;
            lastrow = ((lastrow + 1) > rowLimit && (lastrow + 1) % rowLimit == 0) ? (lastrow + 1) : (int) Math.ceil((lastrow + 1) / rowLimit * 1.0) * rowLimit;
    
            for (int i = 0; i <= maxColum; i = i + columLimit) {
                for (int j = 0; j <= lastrow; j = j + rowLimit) {
                    HSSFClientAnchor wanchor = new HSSFClientAnchor(0, 0, 255, 255, (short) i, j, (short) (i + columLimit - 1), j + rowLimit - 1);
                    list.add(wanchor);
                }
            }
            return list;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441
    • 442
    • 443
    • 444
    • 445
    • 446
    • 447
    • 448
    • 449
    • 450
    • 451
    • 452
    • 453
    • 454
    • 455
    • 456
    • 457
    • 458
    • 459
    • 460
    • 461
    • 462
    • 463
    • 464
    • 465
    • 466
    • 467
    • 468
    • 469
    • 470
    • 471
    • 472
    • 473
    • 474
    • 475
    • 476
    • 477
    • 478
    • 479
    • 480
    • 481
    • 482
    • 483
    • 484
    • 485
    • 486
    • 487
    • 488
    • 489
    • 490
    • 491
    • 492
    • 493
    • 494
    • 495
    • 496
    • 497
    • 498
    • 499
    • 500
    • 501
    • 502
    • 503
    • 504
    • 505
    • 506
    • 507
    • 508
    • 509
    • 510
    • 511
    • 512
    • 513
    • 514
    • 515
    • 516
    • 517
    • 518
    • 519
    • 520
    • 521
    • 522
    • 523
    • 524
    • 525
    • 526
    • 527
    • 528
    • 529
    • 530
    • 531
    • 532
    • 533
    • 534
    • 535
    • 536
    • 537
    • 538
    • 539
    • 540
    • 541
    • 542
    • 543
    • 544
    • 545
    • 546
    • 547
    • 548
    • 549
    • 550
    • 551
    • 552
    • 553
    • 554
    • 555
    • 556
    • 557
    • 558
    • 559
    • 560
    • 561
    • 562
    • 563
    • 564
    • 565
    • 566
    • 567
    • 568
    • 569
    • 570
    • 571
    • 572
    • 573
    • 574
    • 575
    • 576
    • 577
    • 578
    • 579
    • 580
    • 581
    • 582
    • 583
    • 584
    • 585
    • 586
    • 587
    • 588
    • 589
    • 590
    • 591
    • 592
    • 593
    • 594
    • 595
    • 596
    • 597
    • 598
    • 599
    • 600
    • 601
    • 602
    • 603
    • 604
    • 605
    • 606
    • 607
    • 608
    • 609
    • 610
    • 611
    • 612
    • 613
    • 614
    • 615
    • 616
    • 617
    • 618
    • 619
    • 620
    • 621
    • 622
    • 623
    • 624
    • 625
    • 626
    • 627
    • 628
    • 629
    • 630
    • 631
    • 632
    • 633
    • 634
    • 635
    • 636
    • 637
    • 638
    • 639
    • 640
    • 641
    • 642
    • 643
    • 644
    • 645
    • 646
    • 647
    • 648
    • 649
    • 650
    • 651
    • 652
    • 653
    • 654
    • 655
    • 656
    • 657
    • 658

    插入图片的生成水印方式,在【createWaterRemark和circulationPic】两个方法,分别是:

    • 生成水印图片(createWaterRemark)
    • 计算水印图片的覆盖面积(circulationPic)
    • 可打印

    背景图生成水印的方式,参考下面的工具类。

    2.3 背景图生成水印方式

    import cn.hutool.core.date.DatePattern;
    import cn.hutool.core.date.DateUtil;
    import cn.hutool.core.util.ReflectUtil;
    import com.ai.sop.management.constant.WebConstant;
    import com.ai.system.entity.beans.TdSysStaff;
    import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
    import org.apache.poi.hssf.usermodel.HSSFSheet;
    import org.apache.poi.openxml4j.opc.PackagePartName;
    import org.apache.poi.openxml4j.opc.PackageRelationship;
    import org.apache.poi.openxml4j.opc.TargetMode;
    import org.apache.poi.ss.usermodel.Workbook;
    import org.apache.poi.xssf.streaming.SXSSFSheet;
    import org.apache.poi.xssf.streaming.SXSSFWorkbook;
    import org.apache.poi.xssf.usermodel.*;
    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.HttpSession;
    import java.awt.*;
    import java.awt.image.BufferedImage;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    
    
    /**
     * @author daniel
     * @description Excel 添加水印。
     *              支持 SXSSFWorkbook 和 XSSFWorkbook 模式
     *              不支持 HSSFWorkbook 导出的方式
     */
    
    public class WaterMarkUtil {
    
        /**
         * Excel 导出添加水印
         *
         * @param workbook ExcelWorkbook
         */
        public static void insertWaterMarkTextToXlsx(Workbook workbook) throws IOException {
        	 //设置水印内容
            String user = "daniel"; //导出人
            String date = DateUtil.format(new Date(), DatePattern.NORM_DATETIME_FORMAT); //yyyy-MM-dd HH:mm:ss
            String waterMarkText = user + "  " + date;
    
            if (workbook instanceof SXSSFWorkbook) {
                insertWaterMarkTextToXlsx((SXSSFWorkbook) workbook, waterMarkText);
            } else if (workbook instanceof XSSFWorkbook) {
                insertWaterMarkTextToXlsx((XSSFWorkbook) workbook, waterMarkText);
            }
            //throw new RemoteException("HSSFWorkbook 模式不支持 Excel 水印");
        }
    
        /**
         * 给 Excel 添加水印
         *
         * @param workbook      SXSSFWorkbook
         * @param waterMarkText 水印文字内容
         */
        public static void insertWaterMarkTextToXlsx(SXSSFWorkbook workbook, String waterMarkText) throws IOException {
            //设置水印文字内容
            BufferedImage image = createWatermarkImage(waterMarkText);
            ByteArrayOutputStream imageOs = new ByteArrayOutputStream();
            ImageIO.write(image, "png", imageOs);
            int pictureIdx = workbook.addPicture(imageOs.toByteArray(), XSSFWorkbook.PICTURE_TYPE_PNG);
            XSSFPictureData pictureData = (XSSFPictureData) workbook.getAllPictures().get(pictureIdx);
            for (int i = 0; i < workbook.getNumberOfSheets(); i++) {//获取每个Sheet表
                SXSSFSheet sheet =  workbook.getSheetAt(i);
                //这里由于 SXSSFSheet 没有 getCTWorksheet() 方法,通过反射取出 _sh 属性
                XSSFSheet shReflect = (XSSFSheet) ReflectUtil.getFieldValue(sheet, "_sh");
                PackagePartName ppn = pictureData.getPackagePart().getPartName();
                String relType = XSSFRelation.IMAGES.getRelation();
                PackageRelationship pr = shReflect.getPackagePart().addRelationship(ppn, TargetMode.INTERNAL, relType, null);
                shReflect.getCTWorksheet().addNewPicture().setId(pr.getId());
            }
        }
    
    
        /**
         * 给 Excel 添加水印
         *
         * @param workbook      XSSFWorkbook
         * @param waterMarkText 水印文字内容
         */
        public static void insertWaterMarkTextToXlsx(XSSFWorkbook workbook, String waterMarkText) throws IOException {
            BufferedImage image = createWatermarkImage(waterMarkText);
            ByteArrayOutputStream imageOs = new ByteArrayOutputStream();
            ImageIO.write(image, "png", imageOs);
            int pictureIdx = workbook.addPicture(imageOs.toByteArray(), XSSFWorkbook.PICTURE_TYPE_PNG);
            XSSFPictureData pictureData = workbook.getAllPictures().get(pictureIdx);
            for (int i = 0; i < workbook.getNumberOfSheets(); i++) {//获取每个Sheet表
                XSSFSheet sheet = workbook.getSheetAt(i);
                PackagePartName ppn = pictureData.getPackagePart().getPartName();
                String relType = XSSFRelation.IMAGES.getRelation();
                PackageRelationship pr = sheet.getPackagePart().addRelationship(ppn, TargetMode.INTERNAL, relType, null);
                sheet.getCTWorksheet().addNewPicture().setId(pr.getId());
            }
        }
    
        /**
         * 创建水印图片
         *
         * @param waterMark 水印文字
         */
        public static BufferedImage createWatermarkImage(String waterMark) {
            String[] textArray = waterMark.split("\n");
            Font font = new Font("microsoft-yahei", Font.PLAIN, 32);
            int width = 500;
            int height = 400;
    
            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            // 背景透明 开始
            Graphics2D g = image.createGraphics();
            image = g.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
            g.dispose();
            // 背景透明 结束
            g = image.createGraphics();
            g.setColor(new Color(Color.lightGray.getRGB()));// 设定画笔颜色
            g.setFont(font);// 设置画笔字体
            //   g.shear(0.1, -0.26);// 设定倾斜度
    
            //设置字体平滑
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    
            //文字从中心开始输入,算出文字宽度,左移动一半的宽度,即居中
            FontMetrics fontMetrics = g.getFontMetrics(font);
    
            // 水印位置
            int x = width / 2;
            int y = height / 2;
            // 设置水印旋转
            g.rotate(Math.toRadians(-40), x, y);
            for (String s : textArray) {
                // 文字宽度
                int textWidth = fontMetrics.stringWidth(s);
                g.drawString(s, x - (textWidth / 2), y);// 画出字符串
                y = y + font.getSize();
            }
    
            g.dispose();// 释放画笔
            return image;
        }
    
        /**
         * 设置打印的参数
         *
         * @param wb XSSFWorkbook
         */
        public static void setPrintParams(XSSFWorkbook wb) {
            XSSFSheet sheet = wb.getSheetAt(0);
            XSSFPrintSetup printSetup = sheet.getPrintSetup();
            // 打印方向,true:横向,false:纵向(默认
            printSetup.setLandscape(true);
            //设置A4纸
            printSetup.setPaperSize(XSSFPrintSetup.A4_PAPERSIZE);
            // 将整个工作表打印在一页(缩放),如果行数很多的话,可能会出问题
            // sheet.setAutobreaks(true);
            //将所有的列调整为一页,行数多的话,自动分页
            printSetup.setScale((short) 70);//缩放的百分比,自行调整
            sheet.setAutobreaks(false);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167

    此方法在ExcelUtils类中有调用,参考【exportExcelWaterMark】方法。

    三、使用方法

    参考即使用即可,具体情况根据项目内容修改

    	@PostMapping("/roleQueryExport")
    	@ApiOperation(value = "导出excel")
    	public void excelExport(@RequestBody User user, HttpServletResponse response) {
    	String fileName = "导出excel" +".xls";//可以随便写,让前端去控制导出的名字
    	// 列名,表格第一行
    	List<String> columnList = Arrays.asList("列1","列2","列3","列4");
    	
    	try {
    	    List<List<String>> list = userService.excelExport(user);
    	    // 将需要写入Excel的数据传入
    	    //ExcelUtils.exportExcelWaterMark(response, fileName, columnList, list);//不能打印,背景图格式
    	    ExcelUtils.exportExcelWaterMarkPrint(response, fileName, columnList, list);//可打印,插入透明图片格式
    	}catch (Exception e) {
    	    log.info(e.getMessage());
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    其他方式操作excel 文章参考:《EasyExcel导出导入的方式参考链接》

    以上就是使用POI方式导出Excel的基础使用过程,欢迎点赞关注交流。

  • 相关阅读:
    Python垃圾回收机制详解:引用计数与循环垃圾收集器
    iTOP-RK3588开发板rknn_multiple_input_demo 体验
    Linux内核分析与应用6-系统调用
    qt creator5.15.2用的是什么版本的图形api?
    matlab knn方法快速实现,手把手教学
    node快速搭建一个学习资料共享平台
    vue通过span-method合并列之后,合并列显示在中间位置,根据鼠标滑动跟随展示
    泰迪智能科技企业数据挖掘平台使用场景
    LeetCode热题100——动态规划
    2022-08-30 第五组 张明敏 学习笔记
  • 原文地址:https://blog.csdn.net/qq1808814025/article/details/126921681