基于poi的Excel文件导出(固定表头、固定模板)

先给模板样式,如果不是你需要的,就可以关闭此页面了 

 这样固定表头的好处是,表头的设计可以按照自己的喜好任意制定,不依赖于代码,跟代码相关的只是表头与数据的绑定。

需要在resources下放入excel模板,如下图,模板内容如上图所示,注意数据绑定

所需的依赖:

<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.17</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.17</version>
        </dependency>
        
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>4.1.3</version>
        </dependency>

核心代码

import cn.hutool.core.io.IoUtil;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLDecoder;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@RestController
@RequestMapping("/api/exportPersonnelRegister")
public class ExportExcelController {

    @Autowired
    private YjfzglxtServerService yjfzglxtServerService;



    private static final String PATH = System.getProperty("java.io.tmpdir");
    private static final long OVERTIME = 60*60*1000;
    private static final Map<String, Map<String,Object>> MAPDATA = new ConcurrentHashMap<>();
    private static final Thread RMOVETASK = new Thread(new Runnable() {
        @Override
        public void run() {
            while(true){
                if(MAPDATA.isEmpty() == false){
                    Set<String> ids = MAPDATA.keySet();
                    List<String> list = new ArrayList<String>() ;
                    for(String id:ids){
                        Map<String,Object> data = MAPDATA.get(id);
                        try {
                            File file = new File((String)data.get("targetPath"));
                            Date runTime = (Date) data.get("date");
                            long timeDifference = System.currentTimeMillis() - runTime.getTime();
                            if(file.exists()){
                                if(timeDifference > OVERTIME){
                                    if(file.delete()){
                                        list.add(id) ;
                                    }
                                }
                            }else{
                                list.add(id) ;
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    for(String id:list){
                        MAPDATA.remove(id) ;
                    }
                }
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    });

    @PostConstruct
    private void init () { // 此处启动清理线程
        RMOVETASK.start();
    }


    @RequestMapping(value = "/common")
    public void common(HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        Map<String, Object> resMap = new HashMap<>();
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        // 获取查询条件
        Map<String, String[]> param = request.getParameterMap();
        Map<String, Object> queryParams = new HashMap<String, Object>();
        for (String key : param.keySet()) {
            queryParams.put(key, URLDecoder.decode(param.get(key)[0], "UTF-8"));
        }
        String path = ExportExcelController.class.getResource("/excelTemplate/").getPath();


        String templateFilePath = path + "personnelRegister.xlsx";
        String fileName = UUID.randomUUID().toString();
        String targetFilePath = PATH + File.pathSeparator + fileName + ".xlsx";
        //注入业务service,由业务servcie处理返回对应字段的数据,对应的字段和表头字段对应
        List<Map<String, Object>> result = yjfzglxtServerService.exportPersonnelRegister(queryParams);
        //表头对应字段
        String[] fields = new String[] {"name", "sex","age", "createUserName",
                "sojourmHistory","journeyDate1", "journeyWay1","travelToolNo1","goPlace1","toPlace1",
                "journeyDate2", "journeyWay2","travelToolNo2","goPlace2","toPlace2"};
        NewExcelUtil.createExcel(result, URLDecoder.decode(templateFilePath,"UTF-8"), URLDecoder.decode(targetFilePath,"UTF-8"));
        resMap.put("status", 1);
        resMap.put("fileName", fileName);
        this.setDataToMap(fileName, "人员登记信息", URLDecoder.decode(targetFilePath,"UTF-8"));
        download(request, response, fileName);
//        return resMap;
    }
    private void setDataToMap(String id,String fileName,String targetPath){
        Map<String, Object> map = new HashMap<>();
        map.put("id", id);
        map.put("fileName", fileName);
        map.put("targetPath", targetPath);
        map.put("date", new Date());
        MAPDATA.put(id, map);
    }

    
    @RequestMapping(value = "/download")
    public void download(HttpServletRequest request, HttpServletResponse response, String fileName)
            throws  IOException {
        Map<String, Object> data = MAPDATA.get(fileName);
        if(data == null || StringUtils.isEmpty(data.get("targetPath"))){
            response.getWriter().print(
                    "<script language='javascript'>alert('export is error! please try again.');</script>");
            return;
        }
        String filePath = (String) data.get("targetPath");
        File file = new File(filePath);
        FileInputStream fis;
        try {
            response.addCookie(new Cookie("exportExcelToken", "1"));
            OutputStream out = response.getOutputStream();
            fis = new FileInputStream(file);
            response.setContentType("application/octet-stream");
            response.addHeader("Content-Disposition",
                    "attachment; filename=" + java.net.URLEncoder.encode((String)data.get("fileName") + ".xlsx", "UTF-8"));
            IoUtil.copy(fis, out);
            out.flush();
            out.close();
            fis.close();
            if (file.exists()) {
                file.delete();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

 涉及到的工具类

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class NewExcelUtil {
    public static void createExcel(List<Map<String, Object>> result, String templateFilePath, String targetFilePath) {
        File template = new File(templateFilePath);
        if (!template.exists() || !template.isFile()) {
            throw new IllegalArgumentException("导出模板不存在!无法导出成excel");
        }

        ExcelReader templateReader = new ExcelReader(template, 0);
        templateReader.setIgnoreEmptyRow(false);
        List<List<Object>> copys = templateReader.read(0, 10);
        templateReader.close();
        int startIndex = 0;
        List<Object> templateRow = null;
        if (copys != null && copys.size() > 0) {
            for (int i = 0; i < copys.size(); i++) {
                if (checkIsTemplateRow(copys.get(i))) {
                    startIndex = i;
                    templateRow = copys.get(i);
                    break;
                }
            }
        } else {
            templateReader.close();
            throw new IllegalArgumentException("导出模板不符合规范,无法导出!");
        }
        if (templateRow == null) {
            templateReader.close();
            throw new IllegalArgumentException("导出模板未设置替换项,无法导出!");
        }
        System.out.println("the targetFilePath::" + targetFilePath);
        File target = new File(targetFilePath);
        try {
            FileUtil.copy(template, target, true);
        } catch (Exception e) {
            throw new IllegalArgumentException("创建缓存数据出错,无法完成导出!错误:" + e.getMessage());
        }
        ExcelWriter writer = ExcelUtil.getWriter(target);
        writer.passRows(startIndex);
        if (result.size() < 1) {
            result.add(new HashMap<String, Object>());
        }
        int size = result.size();
        for (int i = 0; i < size; i++) {
            Map<String, Object> rowData = result.get(i) instanceof Map ? result.get(i) : objectToMap(result.get(i));
            writer.write(getDataRow(templateRow, rowData));
            if (i % 1000 == 0) {
                writer.flush();
            }
        }
        writer.flush();
        writer.close();
    }

    public static Map<String, Object> objectToMap(Object obj) {
        Map<String, Object> map = null;
        try {
            if (obj == null)
                return null;
            map = new HashMap<String, Object>();
            BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : propertyDescriptors) {
                String key = property.getName();
                if (key.compareToIgnoreCase("class") == 0) {
                    continue;
                }
                Method getter = property.getReadMethod();
                Object value = getter != null ? getter.invoke(obj) : null;
                map.put(key, value);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return map;
    }

    private static List<Object> getDataRow(List<Object> templateRow, Map<String, Object> rowData) {
        List<Object> list = new ArrayList<Object>();
        for (Object tem : templateRow) {
            if (tem != null) {
                if (tem.toString().indexOf("${result.") > -1) {
                    Object obj = rowData.get(tem.toString().replace("${result.", "").replace("}", "").trim());
                    if (obj instanceof Date) {
                        obj = DateUtil.formatDateTime((Date) obj);
                        if (obj != null && obj.toString().indexOf("00:00:00") > -1) {
                            obj = obj.toString().replace("00:00:00", "");
                        }
                    }
                    list.add(obj != null ? obj : "");
                } else if (tem.toString().replaceAll(" ", "").indexOf("${(") > -1 ) {
                    String com = tem.toString().replaceAll(" ", "").replace("${(", "").replace(")}", "");
                    String regex = "result.[A-Za-z0-9]+";
                    Pattern p = Pattern.compile(regex);
                    Matcher matcher = p.matcher(com);
                    Pattern pattern = Pattern.compile("-?[1-9]\d*");
                    Pattern pattern2 = Pattern.compile("-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)");
                    while (matcher.find()) {
                        String resultStr = matcher.group();
                        String param = resultStr.replace("result.", "");
                        Object obj = rowData.get(param);
                        if (obj == null || (!pattern.matcher(obj.toString()).matches() && !pattern2.matcher(obj.toString()).matches())) {
                            obj = "0";
                        }
                        com = com.replace(resultStr, obj.toString());
                    }
                    double result = Calculator.conversion(com);
                    list.add(result);
                } else {
                    list.add(tem.toString());
                }
            } else {
                list.add("");
            }
        }
//        printList(list);
        return list;
    }

    private static void printList(List<Object> list) {
        System.out.println();
        list.forEach(ojb -> {System.out.print("," + ojb.toString());});
    }

//    public static void main(String[] args) {
//        String str = "result.packageFormQuantity*result.packageFormCost+result.dosageFormQuantity*result.dosageFormCost";
//        String regex = "result.[A-Za-z0-9]+";
//        Pattern p = Pattern.compile(regex);
//        Matcher matcher = p.matcher(str);
//        while (matcher.find()) {
//            System.out.println(matcher.group());
//            str = str.replace(matcher.group(), "10");
//        }
//        System.out.println("the over str::" + str);
//        double result = Calculator.conversion(str);
//        System.out.println("the over result::" + result);
//    }

    private static boolean checkIsTemplateRow(List<Object> list) {
        if (list != null && list.size() > 0) {
            for (int i = 0; i < list.size(); i++) {
                Object str = list.get(i);
                if (str != null && str.toString().indexOf("${result.") > -1) {
                    return true;
                }
            }
        }
        return false;
    }
}

其他相关工具类

Calculator工具类:

import java.util.Collections;
import java.util.Stack;


public class Calculator {
    private Stack<String> postfixStack = new Stack<String>();// 后缀式栈
    private Stack<Character> opStack = new Stack<Character>();// 运算符栈
    private int[] operatPriority = new int[] { 0, 3, 2, 1, -1, 1, 0, 2 };// 运用运算符ASCII码-40做索引的运算符优先级

    public static double conversion(String expression) {
        double result = 0;
        Calculator cal = new Calculator();
        try {
            expression = transform(expression);
            result = cal.calculate(expression);
        } catch (Exception e) {
            // e.printStackTrace();
            // 运算错误返回NaN
            return 0.0 / 0.0;
        }
        // return new String().valueOf(result);
        return result;
    }

    /**
     * 将表达式中负数的符号更改
     *
     * @param expression
     *            例如-2+-1*(-3E-2)-(-1) 被转为 ~2+~1*(~3E~2)-(~1)
     * @return
     */
    private static String transform(String expression) {
        char[] arr = expression.toCharArray();
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == '-') {
                if (i == 0) {
                    arr[i] = '~';
                } else {
                    char c = arr[i - 1];
                    if (c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == 'E' || c == 'e') {
                        arr[i] = '~';
                    }
                }
            }
        }
        if(arr[0]=='~'||arr[1]=='('){
            arr[0]='-';
            return "0"+new String(arr);
        }else{
            return new String(arr);
        }
    }

    /**
     * 按照给定的表达式计算
     *
     * @param expression
     *            要计算的表达式例如:5+12*(3+5)/7
     * @return
     */
    public double calculate(String expression) {
        Stack<String> resultStack = new Stack<String>();
        prepare(expression);
        Collections.reverse(postfixStack);// 将后缀式栈反转
        String firstValue, secondValue, currentValue;// 参与计算的第一个值,第二个值和算术运算符
        while (!postfixStack.isEmpty()) {
            currentValue = postfixStack.pop();
            if (!isOperator(currentValue.charAt(0))) {// 如果不是运算符则存入操作数栈中
                currentValue = currentValue.replace("~", "-");
                resultStack.push(currentValue);
            } else {// 如果是运算符则从操作数栈中取两个值和该数值一起参与运算
                secondValue = resultStack.pop();
                firstValue = resultStack.pop();

                // 将负数标记符改为负号
                firstValue = firstValue.replace("~", "-");
                secondValue = secondValue.replace("~", "-");

                String tempResult = calculate(firstValue, secondValue, currentValue.charAt(0));
                resultStack.push(tempResult);
            }
        }
            return Double.valueOf(resultStack.pop());
    }

    /**
     * 数据准备阶段将表达式转换成为后缀式栈
     * 
     * @param expression
     */
    private void prepare(String expression) {
        opStack.push(',');// 运算符放入栈底元素逗号,此符号优先级最低
        char[] arr = expression.toCharArray();
        int currentIndex = 0;// 当前字符的位置
        int count = 0;// 上次算术运算符到本次算术运算符的字符的长度便于或者之间的数值
        char currentOp, peekOp;// 当前操作符和栈顶操作符
        for (int i = 0; i < arr.length; i++) {
            currentOp = arr[i];
            if (isOperator(currentOp)) {// 如果当前字符是运算符
                if (count > 0) {
                    postfixStack.push(new String(arr, currentIndex, count));// 取两个运算符之间的数字
                }
                peekOp = opStack.peek();
                if (currentOp == ')') {// 遇到反括号则将运算符栈中的元素移除到后缀式栈中直到遇到左括号
                    while (opStack.peek() != '(') {
                        postfixStack.push(String.valueOf(opStack.pop()));
                    }
                    opStack.pop();
                } else {
                    while (currentOp != '(' && peekOp != ',' && compare(currentOp, peekOp)) {
                        postfixStack.push(String.valueOf(opStack.pop()));
                        peekOp = opStack.peek();
                    }
                    opStack.push(currentOp);
                }
                count = 0;
                currentIndex = i + 1;
            } else {
                count++;
            }
        }
        if (count > 1 || (count == 1 && !isOperator(arr[currentIndex]))) {// 最后一个字符不是括号或者其他运算符的则加入后缀式栈中
            postfixStack.push(new String(arr, currentIndex, count));
        }

        while (opStack.peek() != ',') {
            postfixStack.push(String.valueOf(opStack.pop()));// 将操作符栈中的剩余的元素添加到后缀式栈中
        }
    }

    /**
     * 判断是否为算术符号
     *
     * @param c
     * @return
     */
    private boolean isOperator(char c) {
        return c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')';
    }

    /**
     * 利用ASCII码-40做下标去算术符号优先级
     *
     * @param cur
     * @param peek
     * @return
     */
    public boolean compare(char cur, char peek) {// 如果是peek优先级高于cur,返回true,默认都是peek优先级要低
        boolean result = false;
        if (operatPriority[(peek) - 40] >= operatPriority[(cur) - 40]) {
            result = true;
        }
        return result;
    }

    /**
     * 按照给定的算术运算符做计算
     *
     * @param firstValue
     * @param secondValue
     * @param currentOp
     * @return
     */
    private String calculate(String firstValue, String secondValue, char currentOp) {
        String result = "";
        switch (currentOp) {
        case '+':
            result = String.valueOf(ArithHelper.add(firstValue, secondValue));
            break;
        case '-':
            result = String.valueOf(ArithHelper.sub(firstValue, secondValue));
            break;
        case '*':
            result = String.valueOf(ArithHelper.mul(firstValue, secondValue));
            break;
        case '/':
            result = String.valueOf(ArithHelper.div(firstValue, secondValue));
            break;
        }
        return result;
    }
}

ArithHelper工具类:

public class ArithHelper {

    // 默认除法运算精度
    private static final int DEF_DIV_SCALE = 16;

    // 这个类不能实例化
    private ArithHelper() {
    }

    /**
     * 提供精确的加法运算。
     *
     * @param v1 被加数
     * @param v2 加数
     * @return 两个参数的和
     */

    public static double add(double v1, double v2) {
        java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
        java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
        return b1.add(b2).doubleValue();
    }

    public static double add(String v1, String v2) {
        java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
        java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
        return b1.add(b2).doubleValue();
    }

    /**
     * 提供精确的减法运算。
     *
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     */

    public static double sub(double v1, double v2) {
        java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
        java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
        return b1.subtract(b2).doubleValue();
    }

    public static double sub(String v1, String v2) {
        java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
        java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
        return b1.subtract(b2).doubleValue();
    }

    /**
     * 提供精确的乘法运算。
     *
     * @param v1
     *            被乘数
     * @param v2
     *            乘数
     * @return 两个参数的积
     */

    public static double mul(double v1, double v2) {
        java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
        java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
        return b1.multiply(b2).doubleValue();
    }

    public static double mul(String v1, String v2) {
        java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
        java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
        return b1.multiply(b2).doubleValue();
    }

    /**
     * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后10位,以后的数字四舍五入。
     *
     * @param v1
     *            被除数
     * @param v2
     *            除数
     * @return 两个参数的商
     */

    public static double div(double v1, double v2) {
        return div(v1, v2, DEF_DIV_SCALE);
    }

    public static double div(String v1, String v2) {
        java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
        java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
        return b1.divide(b2, DEF_DIV_SCALE, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 定精度,以后的数字四舍五入。
     *
     * @param v1 被除数
     * @param v2 除数
     * @param scale 表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     */

    public static double div(double v1, double v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The   scale   must   be   a   positive   integer   or   zero");
        }
        java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
        java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
        return b1.divide(b2, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 提供精确的小数位四舍五入处理。
     *
     * @param v 需要四舍五入的数字
     * @param scale 小数点后保留几位
     * @return 四舍五入后的结果
     */

    public static double round(double v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The   scale   must   be   a   positive   integer   or   zero");
        }
        java.math.BigDecimal b = new java.math.BigDecimal(Double.toString(v));
        java.math.BigDecimal one = new java.math.BigDecimal("1");
        return b.divide(one, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    public static double round(String v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The   scale   must   be   a   positive   integer   or   zero");
        }
        java.math.BigDecimal b = new java.math.BigDecimal(v);
        java.math.BigDecimal one = new java.math.BigDecimal("1");
        return b.divide(one, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
    }
}
原文地址:https://www.cnblogs.com/xuchao0506/p/15029950.html