分类 Java 下的文章

前言

日常开发中,Excel的导出、导入可以说是最常见的功能模块之一,一个通用的、健壮的的工具类可以节省大量开发时间,让我们把更多精力放在业务处理上中

之前我们也写了一个Excel的简单导出,甚至可以不依赖poi,还扩展了纯前端导出Excel!详情请戳:《    POI导出Excel  
 》,遗憾的是这些导出并不支持复杂表头

HExcel,一个简单通用的导入导出Excel工具类  
 1、支持导出复杂表头(支持表头单元格水平合并、垂直合并,支持表头单元格个性化样式)  
 2、支持导入读取sheet数据(只需要提供title与key的关系,不需要管列的顺序)

代码思路都在代码注释里,感兴趣的自己看注释


PS:依赖 poi 以及 hutool

<!-- POI --><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>5.2.3</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.3</version></dependency><!-- hutool --><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.4</version></dependency>

先睹为快

表头目前支持以下属性,可自行扩展:

title  标题
key  key
width  宽度
align 对齐方式
background-color  背景颜色(POI的IndexedColors)
color  字体颜色(POI的IndexedColors)
children  子级表头

导出

代码

//获取HExcel实例HExcel hExcel1 = HExcel.newInstance();//数据,一般是查数据库,经过数据处理生成List<Map<String, Object>> dataList = new ArrayList<>();
HashMap<String, Object> date1 = new HashMap<>();
date1.put("user_name","张三");
date1.put("sex","男");
date1.put("age",20);
date1.put("yu_wen",90);
date1.put("ying_yu",0);
date1.put("shu_xue",85);
date1.put("wu_li",80);
date1.put("total",255);
dataList.add(date1);

HashMap<String, Object> date2 = new HashMap<>();
date2.put("user_name","李四");
date2.put("sex","女");
date2.put("age",18);
date2.put("yu_wen",81);
date2.put("ying_yu",0);
date2.put("shu_xue",90);
date2.put("wu_li",70);
date2.put("total",241);
dataList.add(date2);//如果是固定表头数据,可以在项目资源文件夹下面新建个json文件夹,用来保存表头json数据,方便读、写//JSONArray header = JSONUtil.parseArray(ResourceUtil.readUtf8Str("json/header.json"));//如果是动态表头数据,直接把json字符串写在代码里,方便动态生成表头数据//表头String sheetName = "学生成绩单";
JSONArray headers = JSONUtil.parseArray("" +
        "[\n" +
        "    {\n" +
        "        \"title\":\""+sheetName+"\",\n" +
        "        \"children\":[\n" +
        "            {\n" +
        "                \"title\":\"日期:"+DateUtil.today()+"\",\n" +
        "                \"align\":\"right\",\n" +
        "                \"children\":[\n" +
        "                    {\n" +
        "                        \"title\":\"姓名\",\n" +
        "                        \"key\":\"user_name\",\n" +
        "                    },\n" +
        "                    {\n" +
        "                        \"title\":\"语文\",\n" +
        "                        \"key\":\"yu_wen\",\n" +
        "                    },\n" +
        "                    {\n" +
        "                        \"title\":\"数学\",\n" +
        "                        \"key\":\"shu_xue\",\n" +
        "                    },\n" +
        "                    {\n" +
        "                        \"title\":\"总分\",\n" +
        "                        \"key\":\"total\",\n" +
        "                        \"background-color\":17,\n" +
        "                        \"color\":10,\n" +
        "                        \"width\":30,\n" +
        "                    },\n" +
        "                ]\n" +
        "            },\n" +
        "        ]\n" +
        "    },\n" +
        "]" +
        "");//生成sheethExcel1.buildSheet(sheetName, headers, dataList);//保存成File文件hExcel1.toFile("C:\\Users\\XFT User\\Desktop\\学生成绩单复杂表头导出测试.xls");//关闭对象hExcel1.close();

效果


导入

需要导入的Excel文件

代码

//需要设置title与key的关系JSONObject headerTitleKey = new JSONObject("" +
        "{\n" +
        "    \"姓名\":\"user_name\",\n" +
        "    \"语文\":\"yu_wen\",\n" +
        "    \"数学\":\"shu_xue\",\n" +
        "    \"总分\":\"total\",\n" +
        "}" +
        "");//根据Excel文件,获取HExcel实例HExcel hExcel2 = HExcel.newInstance(new File("C:\\Users\\XFT User\\Desktop\\学生成绩单复杂表头导出测试.xls"));//根据title-key关系,读取指定位置的sheet数据List<Map<String, Object>> sheetList = hExcel2.readSheet(2, 3, headerTitleKey);//打印sheetList数据for (Map<String, Object> map : sheetList) {
    System.out.println(map.toString());
}//关闭对象hExcel2.close();

效果

{user_name=张三, yu_wen=90, shu_xue=85, total=255}  
 {user_name=李四, yu_wen=81, shu_xue=90, total=241}


完整代码


 
 

package cn.huanzi.qch.util;import cn.hutool.json.JSONArray;import cn.hutool.json.JSONObject;import org.apache.poi.hssf.usermodel.*;import org.apache.poi.ss.usermodel.*;import org.apache.poi.ss.util.CellRangeAddress;import javax.servlet.ServletOutputStream;import javax.servlet.http.HttpServletResponse;import java.io.*;import java.net.URLEncoder;import java.util.*;/**
 * HExcel,一个简单通用的导入导出Excel工具类
 * 1、支持复杂表头导出(支持表头单元格水平合并、垂直合并,支持表头单元格个性化样式)
 * 2、支持导入读取sheet数据(只需要提供title与key的关系,不需要管列的顺序)
 *
 * PS:依赖 poi 以及 hutool
 *
 * 详情请戳:https://www.cnblogs.com/huanzi-qch/p/17797355.html
 */public class HExcel {/** * 获取一个HExcel实例,并初始化空Workbook对象     */public static HExcel newInstance(){
        HExcel hExcelUtil = new HExcel();
        hExcelUtil.hSSFWorkbook = new HSSFWorkbook();return hExcelUtil;
    }/** * 获取一个HExcel实例,并根据excelFile初始化Workbook对象     */public static HExcel newInstance(File excelFile){
        HExcel hExcelUtil = new HExcel();try {
            hExcelUtil.hSSFWorkbook = new HSSFWorkbook(new FileInputStream(excelFile));
        } catch (IOException e) {throw new RuntimeException("【HExcel】 根据excelFile初始化Workbook对象异常",e);
        }return hExcelUtil;
    }/** * 导入并读取Excel
     *
     * @param sheetIndex 需要读取的sheet下标
     * @param firstDataRow 数据起始行
     * @param headerTitleKey title与key的关系json对象
     * @return 返回数据集合     */public List<Map<String, Object>> readSheet(int sheetIndex,int firstDataRow,JSONObject headerTitleKey){//最终返回的数据集合ArrayList<Map<String, Object>> list = new ArrayList<>();//获取sheetHSSFSheet sheet = this.hSSFWorkbook.getSheetAt(sheetIndex);//获取title与col的对应关系HashMap<Integer, String> headerMap = new HashMap<>();int lastCellNum = sheet.getRow(0).getLastCellNum();for (int i = 0; i < lastCellNum; i++) {for (int j = firstDataRow-1; j >=0 ; j--) {
                HSSFCell cell = sheet.getRow(j).getCell(i);if(cell != null && !"".equals(cell.getStringCellValue())){
                    String title = cell.getStringCellValue();
                    headerMap.put(i,title);break;
                }
            }
        }//获取数据for (int i = firstDataRow; i <= sheet.getLastRowNum(); i++) {
            HSSFRow row = sheet.getRow(i);
            LinkedHashMap<String, Object> dateMap = new LinkedHashMap<>();for (int j = 0; j < lastCellNum; j++) {
                String title = headerMap.get(j);
                String key = headerTitleKey.getStr(title);if(key != null && !"".equals(key)){
                    String value = row.getCell(j).getStringCellValue();
                    dateMap.put(key,value);
                }
            }
            list.add(dateMap);
        }return list;
    }/** * 构造一个sheet,以及生成复杂表头、表数据
     *
     * @param sheetName sheet名称
     * @param headers 复杂表头json数组对象
     * @param dataLists 表数据集合
     * @return HExcel     */public HExcel buildSheet(String sheetName, JSONArray headers, List<Map<String, Object>> dataLists) {//建立新的sheet对象HSSFSheet sheet = this.hSSFWorkbook.createSheet(sheetName);//设置表单名//生成复杂表头int row = 0;//当前行int col = 0;//当前列HashMap<String, Object> hashMap = createHeader(sheet,row,col,headers);
        ArrayList<String> headerList = (ArrayList<String>) hashMap.get("keyList");
        row = (int) hashMap.get("maxRow");//取出水平合并区域数据List<CellRangeAddress> cellRangeAddressList = sheet.getMergedRegions();//垂直合并,单元格为空,且不属于水平合并区域//这里row-1是因为,生成所有表头结束后,maxRow比最大行+1,for (int i = 0; i < headerList.size(); i++) {for (int j = 0; j <= row-1; j++) {boolean flag = true;//单元格不为空HSSFCell cell = sheet.getRow(j).getCell(i);if(cell != null){continue;
                }//检查合并区域for (CellRangeAddress cellAddresses : cellRangeAddressList) {int OldFirstRow = cellAddresses.getFirstRow();int OldLastRow = cellAddresses.getLastRow();int OldFirstCol = cellAddresses.getFirstColumn();int OldLastCol = cellAddresses.getLastColumn();//与合并区域重叠if ((OldFirstRow >= j && OldLastRow <= j) && (OldFirstCol >= i && OldLastCol <= i)) {
                        flag = false;break;
                    }
                }//满足条件,将上一个单元格与最后一个单元格合并if(flag){
                    mergedCell(sheet,j-1,row-1,i,i);break;
                }
            }
        }//开始填充数据HSSFCellStyle dataStyle = createDataStyle(sheet);for (Map<String, Object> map : dataLists) {//创建内容行HSSFRow dataHSSFRow = sheet.createRow(row);for (int i = 0; i < headerList.size(); i++) {
                String key = headerList.get(i);
                Object val = map.get(key);
                createCell(dataHSSFRow, i, dataStyle, val == null ? "" : String.valueOf(val));
            }
            row++;
        }return this;
    }/** * 保存成File文件
     *
     * @param path 完整文件路径+文件名     */public void toFile(String path) {//try-catch语法糖try (FileOutputStream out = new FileOutputStream(path);){this.hSSFWorkbook.write(out);
        }catch (IOException e){throw new RuntimeException("【HExcel】 Workbook对象文件流写入File异常",e);
        }
    }/** * 保存到HttpServletResponse
     *
     * @param fileName 文件名
     * @param response HttpServletResponse对象     */public void toHttpServletResponse(String fileName, HttpServletResponse response) {//try-catch语法糖try (ServletOutputStream outputStream = response.getOutputStream();){
            response.setHeader("Accept-Ranges", "bytes");
            response.setHeader("Content-disposition", "attachment; filename=\"" + URLEncoder.encode(fileName, "UTF-8") + "\"");
            response.setContentType("application/octet-stream");this.hSSFWorkbook.write(outputStream);
        }catch (Exception e){throw new RuntimeException("【HExcel】 Workbook对象文件流写入Response异常",e);
        }
    }/** * 关闭Workbook     */public void close(){try{//关闭Workbookthis.hSSFWorkbook.close();
        } catch (Exception e) {throw new RuntimeException("【HExcel】 关闭Workbook异常",e);
        }
    }/*          已下设置私有,对外隐藏实现细节           *//** * Workbook对象     */private HSSFWorkbook hSSFWorkbook;/** * 构造表头
     *
     * @param sheet sheet
     * @param row 当前操作行
     * @param col 当前操作列
     * @param headers 表头数据
     * @return 返回一个map对象,供上级表头获取最新当前操作行、列、key集合     */private HashMap<String,Object> createHeader(HSSFSheet sheet, int row, int col, JSONArray headers){//最终返回对象HashMap<String, Object> hashMap = new HashMap<>();//key集合ArrayList<String> keyList = new ArrayList<>();

        HSSFWorkbook wb = sheet.getWorkbook();
        HSSFRow headerHSSFRow = sheet.getRow(row);if(headerHSSFRow == null){
            headerHSSFRow = sheet.createRow(row);
        }for (Object object : headers) {
            JSONObject header = (JSONObject) object;
            String title = (String) header.get("title");
            String key = (String) header.get("key");
            Object width = header.get("width");
            Object align = header.get("align");
            Object backgroundColor = header.get("background-color");
            Object color = header.get("color");
            Object children = header.get("children");//单元格样式HSSFCellStyle headerStyle = createHeaderStyle(sheet);//自定义单元格背景色if(backgroundColor != null){
                headerStyle.setFillForegroundColor(Short.parseShort(backgroundColor+""));
            }//自定义单元格字体颜色if(color != null){
                headerStyle.getFont(wb).setColor(Short.parseShort(color+""));
            }//默认单元格宽度,20sheet.setColumnWidth(col, 20 * 256);if(width != null){//自定义单元格宽度sheet.setColumnWidth(col, (int) width * 256);
            }//默认水平对齐方式(水平居中)if(align != null){//自定义水平对齐方式                HorizontalAlignment alignment;switch (String.valueOf(align).toUpperCase()){case "LEFT":
                        alignment = HorizontalAlignment.LEFT;break;case "RIGHT":
                        alignment = HorizontalAlignment.RIGHT;break;default:
                        alignment = HorizontalAlignment.CENTER;break;
                }
                headerStyle.setAlignment(alignment);
            }//System.out.println(title + " " + key + " " + row + " " + col);//生成单元格同时设置内容            createCell(headerHSSFRow, col, headerStyle, title);//无子级表头if(children == null){//保留顺序,方便后面设置数据                keyList.add(key);//当前列+1col++;
            }//有子级表头else{//递归生成子级表头前,保存父级表头col,用于水平合并int firstCol = col;//递归调用HashMap<String, Object> hashMap1 = createHeader(sheet, row + 1, col, (JSONArray) children);//获取最新col、key集合col = (int) hashMap1.get("col");
                hashMap.put("maxRow",hashMap1.get("maxRow"));
                keyList.addAll((ArrayList<String>) hashMap1.get("keyList"));//水平合并,这里col-1是因为,生成子级表头结束后,col比最后一个下级表头+1,if(!(firstCol == col-1)){
                    mergedCell(sheet,row,row,firstCol,col-1);
                }
            }
        }//将数据设置到对象中,返回上一层hashMap.put("maxRow",(hashMap.get("maxRow") != null ? Integer.parseInt(hashMap.get("maxRow")+"") : 0) + 1);//最大行hashMap.put("row",row);//当前操作行hashMap.put("col",col);//当前操作列hashMap.put("keyList",keyList);//key集合return hashMap;
    }/** * 创建一个单元格
     *
     * @param hSSFRow 当前行对象
     * @param col 当前列
     * @param cellStyle 单元格样式对象
     * @param text 单元格内容,目前只支持字符串,如需支持更多格式可自行扩展     */private void createCell(HSSFRow hSSFRow, int col, HSSFCellStyle cellStyle, String text) {
        HSSFCell cell = hSSFRow.createCell(col);  // 创建单元格cell.setCellStyle(cellStyle); // 设置单元格样式cell.setCellValue(text);  // 设置值    }/** * 构造表头、数据样式
     *
     * @param sheet sheet
     * @return 返回一个单元格样式对象     */private HSSFCellStyle createHeaderStyle(HSSFSheet sheet){
        HSSFWorkbook wb = sheet.getWorkbook();//表头的样式HSSFCellStyle headerStyle = wb.createCellStyle();
        headerStyle.setAlignment(HorizontalAlignment.CENTER);//水平居中headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);//垂直居中//列名的字体HSSFFont dataFont = wb.createFont();
        dataFont.setFontHeightInPoints((short) 12);
        dataFont.setFontName("新宋体");
        headerStyle.setFont(dataFont);// 把字体 应用到当前样式headerStyle.setWrapText(true);//自动换行//填充样式,前景色、天空蓝        headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        headerStyle.setFillForegroundColor(IndexedColors.PALE_BLUE.getIndex());// 设置边框        headerStyle.setBorderBottom(BorderStyle.THIN);
        headerStyle.setBorderLeft(BorderStyle.THIN);
        headerStyle.setBorderRight(BorderStyle.THIN);
        headerStyle.setBorderTop(BorderStyle.THIN);return headerStyle;
    }private HSSFCellStyle createDataStyle(HSSFSheet sheet){
        HSSFWorkbook wb = sheet.getWorkbook();//内容的样式HSSFCellStyle dataStyle = wb.createCellStyle();
        dataStyle.setAlignment(HorizontalAlignment.CENTER);//水平居中dataStyle.setVerticalAlignment(VerticalAlignment.CENTER);//垂直居中//内容的字体HSSFFont font3 = wb.createFont();
        font3.setFontHeightInPoints((short) 12);
        font3.setFontName("新宋体");
        dataStyle.setFont(font3);// 把字体 应用到当前样式dataStyle.setWrapText(true);//自动换行//默认无填充        dataStyle.setFillPattern(FillPatternType.NO_FILL);// 设置边框        dataStyle.setBorderBottom(BorderStyle.THIN);
        dataStyle.setBorderLeft(BorderStyle.THIN);
        dataStyle.setBorderRight(BorderStyle.THIN);
        dataStyle.setBorderTop(BorderStyle.THIN);return dataStyle;
    }/** * 合并单元格
     *
     * @param sheet sheet
     * @param firstRow 起始行
     * @param lastRow 结束行
     * @param firstCol 起始列
     * @param lastCol 结束列     */private void mergedCell(HSSFSheet sheet,int firstRow, int lastRow, int firstCol, int lastCol){//一个单元格无需合并,例如:[0,0,0,0]if(firstRow == lastRow && firstCol == lastCol){return;
        }//先取出合并前的单元格样式HSSFCellStyle cellStyle = sheet.getRow(firstRow).getCell(firstCol).getCellStyle();//合并sheet.addMergedRegion(new CellRangeAddress(firstRow, lastRow, firstCol, lastCol));//解决合并后的边框等样式问题int first;int end;//垂直合并if(firstCol == lastCol){
            first = firstRow;
            end = lastRow+1;for (int i = first; i < end; i++) {
                HSSFRow row = sheet.getRow(i);if(row == null){
                    row = sheet.createRow(i);
                }
                HSSFCell cell = row.getCell(firstCol);if(cell == null){
                    cell = row.createCell(firstCol);
                }
                cell.setCellStyle(cellStyle);
            }
        }//水平合并else{
            first = firstCol;
            end = lastCol+1;for (int i = first; i < end; i++) {
                HSSFRow row = sheet.getRow(firstRow);if(row == null){
                    row = sheet.createRow(firstRow);
                }
                HSSFCell cell = row.getCell(i);if(cell == null){
                    cell = row.createCell(i);
                }
                cell.setCellStyle(cellStyle);
            }
        }
    }

}

 

  View Code  

完整main测试

public static void main(String[] args) {//获取HExcel实例HExcel hExcel1 = HExcel.newInstance();//数据,一般是查数据库,经过数据处理生成List<Map<String, Object>> dataList = new ArrayList<>();
    HashMap<String, Object> date1 = new HashMap<>();
    date1.put("user_name","张三");
    date1.put("sex","男");
    date1.put("age",20);
    date1.put("yu_wen",90);
    date1.put("ying_yu",0);
    date1.put("shu_xue",85);
    date1.put("wu_li",80);
    date1.put("total",255);
    dataList.add(date1);

    HashMap<String, Object> date2 = new HashMap<>();
    date2.put("user_name","李四");
    date2.put("sex","女");
    date2.put("age",18);
    date2.put("yu_wen",81);
    date2.put("ying_yu",0);
    date2.put("shu_xue",90);
    date2.put("wu_li",70);
    date2.put("total",241);
    dataList.add(date2);//如果是固定表头数据,可以在项目资源文件夹下面新建个json文件夹,用来保存表头json数据,方便读、写//JSONArray header = JSONUtil.parseArray(ResourceUtil.readUtf8Str("json/header.json"));//如果是动态表头数据,直接把json字符串写在代码里,方便动态生成表头数据//表头String sheetName = "学生成绩单";
    JSONArray headers = JSONUtil.parseArray("" +
            "[\n" +
            "    {\n" +
            "        \"title\":\""+sheetName+"\",\n" +
            "        \"children\":[\n" +
            "            {\n" +
            "                \"title\":\"日期:"+DateUtil.today()+"\",\n" +
            "                \"align\":\"right\",\n" +
            "                \"children\":[\n" +
            "                    {\n" +
            "                        \"title\":\"姓名\",\n" +
            "                        \"key\":\"user_name\",\n" +
            "                    },\n" +
            "                    {\n" +
            "                        \"title\":\"语文\",\n" +
            "                        \"key\":\"yu_wen\",\n" +
            "                    },\n" +
            "                    {\n" +
            "                        \"title\":\"数学\",\n" +
            "                        \"key\":\"shu_xue\",\n" +
            "                    },\n" +
            "                    {\n" +
            "                        \"title\":\"总分\",\n" +
            "                        \"key\":\"total\",\n" +
            "                        \"background-color\":17,\n" +
            "                        \"color\":10,\n" +
            "                        \"width\":30,\n" +
            "                    },\n" +
            "                ]\n" +
            "            },\n" +
            "        ]\n" +
            "    },\n" +
            "]" +
            "");//生成sheet    hExcel1.buildSheet(sheetName, headers, dataList);//表头JSONArray headers2 = JSONUtil.parseArray("" +
            "[\n" +
            "    {\n" +
            "        \"title\":\"姓名\",\n" +
            "        \"key\":\"user_name\",\n" +
            "    },\n" +
            "    {\n" +
            "        \"title\":\"学科成绩\",\n" +
            "        \"children\":[\n" +
            "            {\n" +
            "                \"title\":\"语文\",\n" +
            "                \"key\":\"yu_wen\",\n" +
            "            },\n" +
            "            {\n" +
            "                \"title\":\"数学\",\n" +
            "                \"key\":\"shu_xue\",\n" +
            "            },\n" +
            "        ]\n" +
            "    },\n" +
            "    {\n" +
            "        \"title\":\"总分\",\n" +
            "        \"key\":\"total\",\n" +
            "        \"align\":\"right\",\n" +
            "        \"background-color\":17,\n" +
            "        \"color\":10,\n" +
            "        \"width\":30\n," +
            "    },\n" +
            "]" +
            "");//生成sheethExcel1.buildSheet("学生成绩单2", headers2, dataList);//表头JSONArray headers3 = JSONUtil.parseArray("" +
            "[\n" +
            "    {\n" +
            "        \"title\":\"姓名\",\n" +
            "        \"key\":\"user_name\"\n" +
            "    },\n" +
            "    {\n" +
            "        \"title\":\"性别\",\n" +
            "        \"key\":\"sex\"\n" +
            "    },\n" +
            "    {\n" +
            "        \"title\":\"年龄\",\n" +
            "        \"key\":\"age\"\n" +
            "    },\n" +
            "    {\n" +
            "        \"title\":\"学科成绩\",\n" +
            "        \"children\":[\n" +
            "            {\n" +
            "                \"title\":\"语言类\",\n" +
            "                \"children\":[\n" +
            "                    {\n" +
            "                        \"title\":\"语文\",\n" +
            "                        \"key\":\"yu_wen\",\n" +
            "                        \"background-color\":7,\n" +
            "                        \"color\":5,\n" +
            "                    },\n" +
            "                  ]\n" +
            "            },\n" +
            "            {\n" +
            "                \"title\":\"科学类\",\n" +
            "                \"background-color\":10,\n" +
            "                \"children\":[\n" +
            "                    {\n" +
            "                        \"title\":\"数学\",\n" +
            "                        \"key\":\"shu_xue\"\n" +
            "                    },\n" +
            "                    {\n" +
            "                        \"title\":\"物理\",\n" +
            "                        \"key\":\"wu_li\"\n" +
            "                    }\n" +
            "                 ]\n" +
            "            },\n" +
            "        ]\n" +
            "    },\n" +
            "    {\n" +
            "        \"title\":\"总分\",\n" +
            "        \"key\":\"total\",\n" +
            "        \"align\":\"right\",\n" +
            "        \"background-color\":17,\n" +
            "        \"color\":10,\n" +
            "        \"width\":30\n," +
            "    },\n" +
            "]"+
            "");//生成sheethExcel1.buildSheet("学生成绩单3", headers3, dataList);//表头JSONArray headers4 = JSONUtil.parseArray("" +
            "[\n" +
            "    {\n" +
            "        \"title\":\"姓名\",\n" +
            "        \"key\":\"user_name\"\n" +
            "    },\n" +
            "    {\n" +
            "        \"title\":\"性别\",\n" +
            "        \"key\":\"sex\"\n" +
            "    },\n" +
            "    {\n" +
            "        \"title\":\"年龄\",\n" +
            "        \"key\":\"age\"\n" +
            "    },\n" +
            "    {\n" +
            "        \"title\":\"学科成绩\",\n" +
            "        \"children\":[\n" +
            "            {\n" +
            "                \"title\":\"语文\",\n" +
            "                \"key\":\"yu_wen\",\n" +
            "            },\n" +
            "            {\n" +
            "                \"title\":\"科学类\",\n" +
            "                \"background-color\":10,\n" +
            "                \"children\":[\n" +
            "                    {\n" +
            "                        \"title\":\"数学\",\n" +
            "                        \"key\":\"shu_xue\"\n" +
            "                    },\n" +
            "                    {\n" +
            "                        \"title\":\"物理\",\n" +
            "                        \"key\":\"wu_li\"\n" +
            "                    }\n" +
            "                 ]\n" +
            "            },\n" +
            "            {\n" +
            "                \"title\":\"英语\",\n" +
            "                \"key\":\"ying_yu\",\n" +
            "            },\n" +
            "        ]\n" +
            "    },\n" +
            "    {\n" +
            "        \"title\":\"总分\",\n" +
            "        \"key\":\"total\",\n" +
            "        \"align\":\"right\",\n" +
            "        \"background-color\":17,\n" +
            "        \"color\":10,\n" +
            "        \"width\":30\n" +
            "      \n" +
            "    }\n" +
            "]"+
            "");//生成sheethExcel1.buildSheet("学生成绩单4", headers4, dataList);//保存成File文件hExcel1.toFile("C:\\Users\\XFT User\\Desktop\\学生成绩单复杂表头导出测试.xls");
    System.out.println("导出完成!\n");//关闭对象    hExcel1.close();//导入//需要设置title与key的关系JSONObject headerTitleKey = new JSONObject("" +
            "{\n" +
            "    \"姓名\":\"user_name\",\n" +
            "    \"语文\":\"yu_wen\",\n" +
            "    \"数学\":\"shu_xue\",\n" +
            "    \"总分\":\"total\",\n" +
            "}" +
            "");//根据Excel文件,获取HExcel实例HExcel hExcel2 = HExcel.newInstance(new File("C:\\Users\\XFT User\\Desktop\\学生成绩单复杂表头导出测试.xls"));//根据title-key关系,读取指定位置的sheet数据List<Map<String, Object>> sheetList = hExcel2.readSheet(2, 3, headerTitleKey);//打印sheetList数据System.out.println("导入完成!");for (Map<String, Object> map : sheetList) {
        System.out.println(map.toString());
    }//关闭对象    hExcel2.close();

}


后记

  一个简单通用的导入导出  
   Excel  
   工具类暂时先记录到这,后续再进行补充  


一 回调方法定义

控制器中定义回调方法

@GetMapping("callback")public String callback(String code, String state, HttpSession session) {System.out.println("callback被调用");System.out.println("code:" + code);System.out.println("state:" + state);return null;}

用户点击“确认登录”后,微信服务器会向谷粒学院的业务服务器发起回调,回调地址就是yml中配置的redirecturi。

二 内网穿透

1 开发步骤

步骤:开通并启动内网穿透ngrok > 开放平台配置回调地址 > yml配置

yml配置:

wx:open:# 微信开放平台 appidappId: <微信开放平台 appid># 微信开放平台 appsecretappSecret: <微信开放平台 appsecret># 微信开放平台 重定向url(guli.shop需要在微信开放平台配置)redirectUri: <微信中你配的回调地址>

注意:yml文件中redirecturi的域名必须和开放平台中应用配置的授权回调域的值完全一致,

但是开放平台上的一个应用只能配置一个回调地址,提供给一个开发者使用。

2 开通方式

ngnok的地址:http://ngrok.cc

开通方式:

3 使用方式

下载客户端,启动客户端

4 原理说明

三 外网服务器跳转

解决多人无法共享回调域设置的问题。

步骤:将跳转程序部署到外网服务器 > 开放平台配置回调地址 > yml配置

跳转程序:部署在guli.shop上

guli.shop服务器的接口可以接收微信的回调请求,将微信回调请求转发到开发者的localhost的8160端口,并传递code和state参数

开放平台配置:

授权回调域一般设置为一个内网穿透地址,例如使用ngrok工具申请一个内网穿透地止

yml配置

wx:open:# 微信开放平台 appidappId: wxed9954c01bb89b47# 微信开放平台 appsecretappSecret: a7482517235173ddb4083788de60b90e# 微信开放平台 重定向url(guli.shop需要在微信开放平台配置)redirectUri: http://guli.shop/api/ucenter/wx/callback8160

四 测试回调跳转服务器

访问回调服务器

http://guli.shop/api/ucenter/wx/callback8160?code=1234&state=666

跳转到

http://localhost:8160/api/ucenter/wx/callback?code=1234&state=666


本课程主要如何利用Java类库中提供的InputStream类,并基于Java编程实现文件信息的读取,以及三个read()方法与OutputStream类中三个write()方法的操作区别形式



OutputStream抽象类对应的另外一个操作类就属于java.io.InputStream类,这个类可以基于字节的方式实现指定输入流数据的读取操作,此类的定义如下:



public abstract class InputStream extends Object implements Closeable



InputStream类也同样实现了Closeable接口(是在JDK后续版本中添加的),而对于InputStream类来讲其提供有如下的数据读取的处理方法。



OutputStream类中提供有三个write()方法,反过来在InputStream类中又提供有三个read()方法,无论从方法的名称上以及参数的类型及个数上都是非常对称的,下面通过一些具体的图示来分析这三个读取数据操作方法的区别所在。



范例:通过InputStream实现数据的读取



package com.yootk.demo;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class YootkDemo { // 李兴华编程训练营:yootk
public static void main(String[] args) throws Exception {
File file = 
new File("H:" + File.separator "muyan" + File.separator "vip" + File.separator "yootk.txt");
if (file.exists()) { // 文件存在
try (InputStream input = new FileInputStream(file)) {
byte data[] = new byte[1024]; // 开辟1K的空间进行读取
int len = input.read(data); // 读取数据到字节数组并返回读取个数
System.out.println("读取到的数据内容【new String(data, 0, len) + "");
catch (Exception e) {}
}
}
}



此时的程序已经实现了整个数据内容的读取,但是这里面还会存在有另外一个问题,如果说此时要读取的内容很大,但是所开辟的数组空间很小,这个时候肯定无法一次性进行数据的读取,那么就需要重复多次读取,而如果文件已经读取到底部的时候,调用read()方法返回的内容就是“-1”(不再是读取到的字节个数了)。



范例:执行数据的多次读取



package com.yootk.demo;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class YootkDemo { // 李兴华编程训练营:yootk
public static void main(String[] args) throws Exception {
StringBuffer buffer = 
new StringBuffer() ; // 保存读取到的内容
File file = new File("H:" + File.separator "muyan" + File.separator "vip" + File.separator "yootk.txt");
if (file.exists()) { // 文件存在
try (InputStream input = new FileInputStream(file)) {
byte data[] = new byte[8]; // 开辟1K的空间进行读取
int len = ;// 保存数据读取的个数
do {
len = input.read(data); 
// 读取数据到字节数组并返回读取个数
if (len != -1) {
buffer.append(
new String(data, 0, len)); // 每次读取到的内容保存在缓冲流中
}
while (len != -1) ; // 没有读取到底
System.out.println("读取到的数据内容【+ buffer + "");
catch (Exception e) {
e.printStackTrace();
}
}
}
}



以上的程序由于一次性的读取不可能将所有的内容全部读取完成,所以采用的方式就是进行部分读取的处理,而在进行部分读取的时候又需要将每一次读取到的内容保存在StringBuffer之中,但是在保存的时候必须保证有内容读取的时候(len != -1)才可以实现数据的添加,只不过这上面的代码有些过于繁琐了,而实际的项目开发过程之中,对于此类的操作一般采用的都是while循环的结构方式。



byte data[] = new byte[8]; // 开辟1K的空间进行读取
int len = ;// 保存数据读取的个数
// 第一个表达式:input.read(data),将输入流的数据读取到字节数组之中
// 第二个表达式:len = input.read(data),将读取到的数据长度赋值给len变量
// 第三个表达式:(len = input.read(data)) != -1,判断len的内容是否不为-1
while ((len = input.read(data)) != -1) {
buffer.append(new String(data, 0, len));
}



相比较之前学习到的do…while处理结构,使用while循环的模式代码会更加的简洁,而这种读取模式是在以后进行IO操作中最为常见的原始方案。