1.添加pom.xml依赖
<properties>
        <java.version>1.8</java.version>
        <hutool-poi.version>5.7.13</hutool-poi.version>
        <poi.version>4.1.2</poi.version>
    </properties>
 <dependencies>
<dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-core</artifactId>
            <version>${hutool-poi.version}</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-poi</artifactId>
            <version>${hutool-poi.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>${poi.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.1.2</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>ooxml-schemas</artifactId>
            <version>1.4</version>
        </dependency>
        </dependencies>
2.编写代码文件
ExcelUtil.java文件
package com.cfm.demo.filedown;
import cn.hutool.poi.excel.BigExcelWriter;
import lombok.Synchronized;
public class ExcelUtil {
    private static final String PATH = "/excel/";
    private static final String SUFFIX = ".xlsx";
    // Excel 列宽
    private static final int COLUMN_WIDTH = 20;
    @Synchronized
    public static BigExcelWriter createBigExcelWriter(String fileName , int columnSize){
        BigExcelWriter bigWriter = cn.hutool.poi.excel.ExcelUtil.getBigWriter(PATH + fileName + SUFFIX);
        for (int size = columnSize; size >= 0 ;size--){
            bigWriter.setColumnWidth(size,COLUMN_WIDTH);
        }
        return bigWriter;
    }
}
FileTest05.java文件
package com.cfm.demo.filedown;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.poi.excel.BigExcelWriter;
import com.alibaba.fastjson.JSONObject;
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.XSSFPictureData;
import org.apache.poi.xssf.usermodel.XSSFRelation;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
@Controller
@RequestMapping("/file")
public class FileTest05 {
    
    /**
     * 文件下载
     */
    @RequestMapping("/down5")
    public static void downloadFIle(HttpServletRequest request, HttpServletResponse response) {
       //数据准备工作
        List<Object> list = new ArrayList<>();
        Map<String, Object> map1 = new HashMap<>();
        map1.put("kk.id", 11);
        map1.put("kk.name", "张三");
        map1.put("kk.address", "北京");
        map1.put("kk.age", "36");
        Map<String, Object> map2 = new HashMap<>();
        map2.put("kk.id", 22);
        map2.put("kk.name", "李四");
        map2.put("kk.address", "上海");
        map2.put("kk.age", "46");
        list.add(map1);
        list.add(map2);
        System.out.println(JSONObject.toJSONString(list));
        /
        String fileName = "文件名字";
        // 创建一个Writer流
        BigExcelWriter bigExcelWriter = ExcelUtil.createBigExcelWriter(fileName, 3);
        //BigExcelWriter bigExcelWriter = new BigExcelWriter();
        //水印的内容
        String watermarkInfo = "我是管理员呀";
        try {
            //添加水印
            Workbook workbook = bigExcelWriter.getWorkbook();
            if (workbook instanceof SXSSFWorkbook) {
                insertWaterMarkTextToXlsx((SXSSFWorkbook) workbook, watermarkInfo);
            } else if (workbook instanceof XSSFWorkbook) {
                insertWaterMarkTextToXlsx((XSSFWorkbook) workbook, watermarkInfo);
            }
            // 获取下Header,判断是哪种浏览器下载文件,来解决文件名的编码命名问题
            String header = request.getHeader("User-Agent").toUpperCase();
            if (header.contains("MSIE") || header.contains("TRIDENT") || header.contains("EDGE")) {
                fileName = URLEncoder.encode(fileName, "utf-8");
                fileName = fileName.replace("+", "%20");    //IE下载文件名空格变+号问题
            } else {
                fileName = new String(fileName.getBytes(), "UTF8");
            }
            // 有点重复,可以不重写上面的ExcelUtils类,这是防止数据量很大时,需要提前准备文件,并不是即时写入即时返回
            String fileName1 = fileName + ".xlsx";
            response.reset();
            // 设置文件对应后缀的ContentType
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            // 返回的是流的形式
            response.setContentType("application/octet-stream; charset=utf-8");
            response.setHeader("Location", fileName1);
            response.setHeader("Cache-Control", "max-age=0");
            // 这句的作用是会打开用户那边下载文件时的那个保存位置的框,如果文件名中有中文,建议编下码
            response.setHeader("Content-disposition", "attachment; filename=" + URLEncoder.encode(fileName1, "UTF8"));
            AtomicInteger count = new AtomicInteger();
            // 开始准备写入数据
            for (Object obj : list) {
                // 将查询的数据转换为map
                Map<String, Object> map = (Map<String, Object>) obj;
                bigExcelWriter.writeRow(map, count.getAndIncrement() == 0);
            }
            bigExcelWriter.close();
            // 上面的过程就是文件的写入过程并且保存在了/excel/目录下
            // 下面就是把这个文件读取出来,以流的形式返回
            File excelFile = new File("/excel/" + fileName1);
            FileInputStream fis = new FileInputStream(excelFile);
            // 文件流上面加一层缓冲流,目的为了加快速度,减少IO
            BufferedInputStream buff = new BufferedInputStream(fis);
            // 同理,输出流上也套了一层缓冲的输出流
            BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
            byte[] car = new byte[1024];
            int l = 0;
            // 按字节读取文件
            while (l < excelFile.length()) {
                int j = buff.read(car, 0, 1024);
                l += j;
                // 写入out流中
                out.write(car, 0, j);
            }
            // 关闭流
            fis.close();
            buff.close();
            out.close();
            excelFile.delete();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 给excel添加水印.
     *
     * @param workbook      Xlsx的Excel工作簿.
     * @param waterMarkText 水印内容.
     * @throws IOException IO异常抛出
     */
    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);
        //获取每个Sheet表
        for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
            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());
        }
    }
    /**
     * 生成水印图片.
     *
     * @param content 需要生成水印的文字信息.
     * @return 图片字节流.
     */
    public static BufferedImage createWatermarkImage(String content) {
        final String[] textArray = content.split("\n");
        //设置水印字体、大小
        final Font font = new Font("TimesRoman", Font.PLAIN, 80);
        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;
    }
    /**
     * 给excel添加水印.
     *
     * @param workbook      Xls的Excel工作簿.
     * @param waterMarkText 水印内容.
     * @throws IOException IO异常抛出
     */
    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);
        //获取每个Sheet表
        for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
            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());
        }
    }
}
3.效果展示
启动本地服务,访问地址后下载的效果:

原文链接:https://blog.csdn.net/qidaihuimou/article/details/128805854























