精华内容
下载资源
问答
  • Javaexcel表格操作

    万次阅读 2018-01-03 10:42:41
    一、介绍 当前B/S模式已成为应用开发的主流,而在企业办公系统中,常常有客户这样子要求...目前,比较常用的实现Java导入、导出Excel的技术有两种Jakarta POI和Java Excel下面我就分别讲解一下如何使用这两个技术实现

    一、介绍
    当前B/S模式已成为应用开发的主流,而在企业办公系统中,常常有客户这样子要求:你要把我们的报表直接用Excel打开(电信系统、银行系统)。或者是:我们已经习惯用Excel打印。这样在我们实际的开发中,很多时候需要实现导入、导出Excel的应用。

    目前,比较常用的实现Java导入、导出Excel的技术有两种Jakarta POI和Java Excel

    下面我就分别讲解一下如何使用这两个技术实现导入、导出Excel

    二、使用Jakarta POI导入、导出Excel
    Jakarta POI 是一套用于访问微软格式文档的Java API。Jakarta POI有很多组件组成,其中有用于操作Excel格式文件的HSSF和用于操作Word的HWPF,在各种组件中目前只有用于操作Excel的HSSF相对成熟。官方主页http://poi.apache.org/index.html,API文档http://poi.apache.org/apidocs/index.html

    2.1 环境配置

    2.1.1下载jar
    官方下载:http://poi.apache.org/download.html这里可以下载到它的最新版本和文档,目前最新版本是3.7,这里使用比较稳定的3.6版。

    2.1.2加入jar包
    将根目录下的poi-3.6-20091214.jar和Lib目录下三个通用包 commons-logging-1.1.jar junit-3.8.1.jar log4j-1.2.13.jar拷贝到项目的Lib下

    2.2 Jakarta POI HSSF API组件

    HSSF(用于操作Excel的组件)提供给用户使用的对象在rg.apache.poi.hssf.usermodel包中,主要部分包括Excel对象,样式和格式,还有辅助操作。有以下几种对象:

    常用组件:

    HSSFWorkbook excel的文档对象

    HSSFSheet excel的表单

    HSSFRow excel的行

    HSSFCell excel的格子单元

    HSSFFont excel字体

    HSSFDataFormat 日期格式

    HSSFHeader sheet头

    HSSFFooter sheet尾(只有打印的时候才能看到效果)

    样式:

    HSSFCellStyle cell样式

    辅助操作包括:

    HSSFDateUtil 日期

    HSSFPrintSetup 打印

    HSSFErrorConstants 错误信息表

    2.3 基本操作步骤

    首先,理解一下一个Excel的文件的组织形式,一个Excel文件对应于一个workbook(HSSFWorkbook),一个workbook可以有多个sheet(HSSFSheet)组成,一个sheet是由多个row(HSSFRow)组成,一个row是由多个cell(HSSFCell)组成。

    基本操作步骤:

    1、用HSSFWorkbook打开或者创建“Excel文件对象”

    2、用HSSFWorkbook对象返回或者创建Sheet对象

    3、用Sheet对象返回行对象,用行对象得到Cell对象

    4、对Cell对象读写。

    下面来看一个动态生成Excel文件的例子:

    //创建HSSFWorkbook对象  
    HSSFWorkbook wb = new HSSFWorkbook();  
    //创建HSSFSheet对象  
    HSSFSheet sheet = wb.createSheet("sheet0");  
    //创建HSSFRow对象  
    HSSFRow row = sheet.createRow(0);  
    //创建HSSFCell对象  
    HSSFCell cell=row.createCell(0);  
    //设置单元格的值  
    cell.setCellValue("单元格中的中文");  
    //输出Excel文件  
    FileOutputStream output=new FileOutputStream("d:\\workbook.xls");  
    wkb.write(output);  
    output.flush();  

    HSSF读取文件同样还是使用这几个对象,只是把相应的createXXX方法变成了getXXX方法即可。可见只要理解了其中原理,不管是读还是写亦或是特定格式都可以轻松实现,正所谓知其然更要知其所以然。

    2.4 导出Excel应用实例

    在2.3中我们寥寥几行代码实际上就已经就是实现了导出Excel一个简单示例,下面我们在看如何实现导出如图所示的Excel表格?

    代码如下:(实际开发中应封装到业务层组件中,然后在控制层中调用。这里直接写在控制层组件,如Servlet的doGet/doPost方法或Struts框架的execute方法中)

    //创建HSSFWorkbook对象(excel的文档对象)  
          HSSFWorkbook wb = new HSSFWorkbook();  
    //建立新的sheet对象(excel的表单)  
    HSSFSheet sheet=wkb.createSheet("成绩表");  
    //在sheet里创建第一行,参数为行索引(excel的行),可以是065535之间的任何一个  
    HSSFRow row1=sheet.createRow(0);  
    //创建单元格(excel的单元格,参数为列索引,可以是0255之间的任何一个  
    HSSFCell cell=row1.createCell(0);  
          //设置单元格内容  
    cell.setCellValue("学员考试成绩一览表");  
    //合并单元格CellRangeAddress构造参数依次表示起始行,截至行,起始列, 截至列  
    sheet.addMergedRegion(new CellRangeAddress(0,0,0,3));  
    //在sheet里创建第二行  
    HSSFRow row2=sheet.createRow(1);      
          //创建单元格并设置单元格内容  
          row2.createCell(0).setCellValue("姓名");  
          row2.createCell(1).setCellValue("班级");      
          row2.createCell(2).setCellValue("笔试成绩");  
    row2.createCell(3).setCellValue("机试成绩");      
          //在sheet里创建第三行  
          HSSFRow row3=sheet.createRow(2);  
          row3.createCell(0).setCellValue("李明");  
          row3.createCell(1).setCellValue("As178");  
          row3.createCell(2).setCellValue(87);      
          row3.createCell(3).setCellValue(78);      
      //.....省略部分代码  
    
    
    //输出Excel文件  
        OutputStream output=response.getOutputStream();  
        response.reset();  
        response.setHeader("Content-disposition", "attachment; filename=details.xls");  
        response.setContentType("application/msexcel");          
        wkb.write(output);  
        output.close();  
    retrun null;  

    加下划线这部分代码是B/S模式中采用的输出方式,而不是输出到本地指定的磁盘目录。该代码表示将details.xls的Excel文件通过应答实体(response)输出给请求的客户端浏览器,客户端可保存或直接打开。

    2.5 样式设置

    在实际应用中导出的Excel文件往往需要阅读和打印的,这就需要对输出的Excel文档进行排版和样式的设置,主要操作有合并单元格、设置单元格样式、设置字体样式等。

    2.5.1单元格合并
    使用HSSFSheet的addMergedRegion()方法

    public int addMergedRegion(CellRangeAddress region)  
    参数CellRangeAddress 表示合并的区域,构造方法如下:
    
    CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol)  

    构造参数依次表示起始行,截至行,起始列, 截至列。示例代码参照2.4部分

    2.5.2设置单元格的行高、列宽

    HSSFSheet sheet=wb.createSheet();  
    
    
    
    sheet.setDefaultRowHeightInPoints(10);//设置缺省列高sheet.setDefaultColumnWidth(20);//设置缺省列宽  
    
    //设置指定列的列宽,256 * 50这种写法是因为width参数单位是单个字符的256分之一  
    
    sheet.setColumnWidth(cell.getColumnIndex(), 256 * 50);  
     ```
    2.5.2单元格样式
    1、创建HSSFCellStyle
    
    
    

    HSSFCellStyle cellStyle=wkb.createCellStyle()
    “`

    2、设置样式

    // 设置单元格的横向和纵向对齐方式,具体参数就不列了,参考HSSFCellStyle

    cellStyle.setAlignment(HSSFCellStyle.ALIGN_JUSTIFY);

    cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);

    /* 设置单元格的填充方式,以及前景颜色和背景颜色

    三点注意:

    1.如果需要前景颜色或背景颜色,一定要指定填充方式,两者顺序无所谓;

    2.如果同时存在前景颜色和背景颜色,前景颜色的设置要写在前面;

    3.前景颜色不是字体颜色。

    */

    //设置填充方式(填充图案)

    cellStyle.setFillPattern(HSSFCellStyle.DIAMONDS);

    //设置前景色

    cellStyle.setFillForegroundColor(HSSFColor.RED.index);

    //设置背景颜色

    cellStyle.setFillBackgroundColor(HSSFColor.LIGHT_YELLOW.index);

    // 设置单元格底部的边框及其样式和颜色

    // 这里仅设置了底边边框,左边框、右边框和顶边框同理可设

    cellStyle.setBorderBottom(HSSFCellStyle.BORDER_SLANTED_DASH_DOT);

    cellStyle.setBottomBorderColor(HSSFColor.DARK_RED.index);

    //设置日期型数据的显示样式

    cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat(“m/d/yy h:mm”));

    3、将样式应用于单元格

    [java] view plain copy print?
    cell.setCellStyle(cellStyle);

    //将样式应用到行,但有些样式只对单元格起作用

    row.setRowStyle(cellStyle);

    2.5.2设置字体样式
    1、创建HSSFFont对象(调用HSSFWorkbook 的createFont方法)

    [java] view plain copy print?
    HSSFWorkbook wb=new HSSFWorkbook();

    HSSFFont fontStyle=wb.createFont();

    HSSFWorkbook wb=new HSSFWorkbook ();

    2、设置字体各种样式

    [java] view plain copy print?
    //设置字体样式

    fontStyle.setFontName(“宋体”);

    //设置字体高度

    fontStyle.setFontHeightInPoints((short)20);

    //设置字体颜色

    font.setColor(HSSFColor.BLUE.index);

    //设置粗体

    fontStyle.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);

    //设置斜体

    font.setItalic(true);

    //设置下划线

    font.setUnderline(HSSFFont.U_SINGLE);

    3、将字体设置到单元格样式

    [java] view plain copy print?
    //字体也是单元格格式的一部分,所以从属于HSSFCellStyle

    // 将字体对象赋值给单元格样式对象

    cellStyle.setFont(font);

    // 将单元格样式应用于单元格

    cell.setCellStyle(cellStyle);

    2.6 导入Excel应用实例

    实现将已存在的Excel文件中的数据导入到系统中的基本步骤同导出十分的类似,关键在于要了解要导入Excel文件的结构,比如数据有哪些列、读取数据起始位置(有效数据从第几行几列开始)等。在实际项目中由于这些数据(Excel文件)往往来自于日常办公人员或其他系统平台产生的业务数据,因此这些Excel文件的数据格式要有统一的要求,并提供访问接口(指访问途径),这样在所需数据的系统中就可通过提供这个访问接口调用方法,从而获得数据。解决方案采用Web Service是不错的选择。这里,我们就以导入2..4所产生的excel表为例,重点掌握如何编写导入Excel代码

    [java] view plain copy print?
    public List loadScoreInfo(String xlsPath) throws IOException{
    List temp = new ArrayList();
    FileInputStream fileIn = new FileInputStream(xlsPath);
    //根据指定的文件输入流导入Excel从而产生Workbook对象
    Workbook wb0 = new HSSFWorkbook(fileIn);
    //获取Excel文档中的第一个表单
    Sheet sht0 = wb0.getSheetAt(0);
    //对Sheet中的每一行进行迭代
    for (Row r : sht0) {
    //如果当前行的行号(从0开始)未达到2(第三行)则从新循环
    If(r.getRowNum()<1){
    continue;
    }
    //创建实体类
    ScoreInfo info=new ScoreInfo();
    //取出当前行第1个单元格数据,并封装在info实体stuName属性上
    info.setStuName(r.getCell(0).getStringCellValue());
    info.setClassName(r.getCell(1).getStringCellValue());
    info.setRscore(r.getCell(2).getNumericCellValue());
    info.setLscore(r.getCell(3).getNumericCellValue());
    temp.add(info);
    }
    fileIn.close();
    return temp;
    }

    三、使用java Excel操作Excel文件
    Java Excel是一开放源码项目,通过它Java开发人员可以读取Excel文件的内容、创建新的Excel文件、更新已经存在的Excel文件。jxl 由于其小巧 易用的特点, 逐渐已经取代了 POI-excel的地位, 成为了越来越多的java开发人员生成excel文件的首选。Java Excel的特征:

    ● 支持Excel 95-2000的所有版本
    ● 生成Excel 2000标准格式
    ● 支持字体、数字、日期格式化操作
    ● 支持对单元格加阴影和加色彩;

    ● 修改存在的工作表;
    ● 支持图像和图表

    ● 日志记录可以定制

    ● 更小更快更省内存

    应该说以上功能已经能够大致满足我们的需要。最关键的是这套API是纯Java的,并不依赖Windows系统,即使运行在Linux下,它同样能够正确的处理Excel文件。另外需要说明的是,这套API对图形和图表的支持很有限,而且仅仅识别PNG格式。在线帮助文档http://jexcelapi.sourceforge.net/resources/javadocs/2_6_10/docs/index.html

    在这里我们将通过一些实例,学习掌握读取、新建、更新,其中也包括常见格式的设置:字体、颜色、背景、合并单元格等操作,有这些其实已经基本足够应付大部分问题了。

    3.1环境配置

    3.1.1下载
    下载地址 http://www.andykhan.com/jexcelapi/

    3.1.2 加入jar包
    将jxl.jar拷贝到项目的Lib下

    3.2 使用Java Excel Api 导出 Excel文件

    下面我们在看如何使用Java Excel实现导出Excel表格?

    代码如下:(实际开发中应封装到业务层组件中,然后在控制层中调用。这里直接写在控制层组件,如Servlet的doGet/doPost方法或Struts框架的execute方法中)

    [java] view plain copy print?
    //获得输出流,该输出流的输出介质是客户端浏览器

    OutputStream output=response.getOutputStream();

    response.reset();

    response.setHeader(“Content-disposition”,”attachment; filename=temp.xls”);

    response.setContentType(“application/msexcel”);

    //创建可写入的Excel工作薄,且内容将写入到输出流,并通过输出流输出给客户端浏览

    WritableWorkbook wk=Workbook.createWorkbook(output);

    ///创建可写入的Excel工作表

    WritableSheet sheet=wk.createSheet("成绩表", 0);  
    

    //把单元格(column, row)到单元格(column1, row1)进行合并。

    //mergeCells(column, row, column1, row1);

    sheet.mergeCells(0,0, 4,0);//单元格合并方法

    //创建WritableFont 字体对象,参数依次表示黑体、字号12、粗体、非斜体、不带下划线、亮蓝色

    WritableFont titleFont=new WritableFont(WritableFont.createFont(“黑体”),12,WritableFont.BOLD,false,UnderlineStyle.NO_UNDERLINE,Colour.LIGHT_BLUE);

    //创建WritableCellFormat对象,将该对象应用于单元格从而设置单元格的样式

    WritableCellFormat titleFormat=new WritableCellFormat();

    //设置字体格式

    titleFormat.setFont(titleFont);

    //设置文本水平居中对齐

    titleFormat.setAlignment(Alignment.CENTRE);

    //设置文本垂直居中对齐

    titleFormat.setVerticalAlignment(VerticalAlignment.CENTRE);

    //设置背景颜色

    titleFormat.setBackground(Colour.GRAY_25);

    //设置自动换行

    titleFormat.setWrap(true);

    //添加Label对象,参数依次表示在第一列,第一行,内容,使用的格式

    Label lab_00=new Label(0,0,”学员考试成绩一览表”,titleFormat);

    //将定义好的Label对象添加到工作表上,这样工作表的第一列第一行的内容为‘学员考试成绩一览表’并应用了titleFormat定义的样式

    sheet.addCell(lab_00);

    WritableCellFormat cloumnTitleFormat=new WritableCellFormat();

    cloumnTitleFormat.setFont(new WritableFont(WritableFont.createFont(“宋体”),10,WritableFont.BOLD,false));

    cloumnTitleFormat.setAlignment(Alignment.CENTRE);

    Label lab_01=new Label(0,1,”姓名”,cloumnTitleFormat);

    Label lab_11=new Label(1,1,”班级”,cloumnTitleFormat);

    Label lab_21=new Label(2,1,”笔试成绩”,cloumnTitleFormat);

    Label lab_31=new Label(3,1,”上机成绩”,cloumnTitleFormat);

    Label lab_41=new Label(4,1,”考试日期”,cloumnTitleFormat);

    sheet.addCell(lab_01);

    sheet.addCell(lab_11);

    sheet.addCell(lab_21);

    sheet.addCell(lab_31);

    sheet.addCell(lab_41);

    sheet.addCell(new Label(0,2,”李明”));

    sheet.addCell(new Label(1,2,”As178”));

    //定义数字格式

    NumberFormat nf=new NumberFormat(“0.00”);

    WritableCellFormat wcf=new WritableCellFormat(nf);

    //类似于Label对象,区别Label表示文本数据,Number表示数值型数据

    Number numlab_22=new Number(2,2,78,wcf);

    sheet.addCell(numlab_22);

    sheet.addCell(newNumber(3,2,87,new WritableCellFormat(new NumberFormat(“#.##”) )));

    //定义日期格式

    DateFormat df=new DateFormat(“yyyy-MM-dd hh:mm:ss”);

    //创建WritableCellFormat对象

    WritableCellFormat datewcf=new WritableCellFormat(df);

    //类似于Label对象,区别Label表示文本数据,DateTime表示日期型数据

    DateTime dtLab_42=new DateTime(4,2,new Date(),datewcf);

    sheet.addCell(dtLab_42);

    //将定义的工作表输出到之前指定的介质中(这里是客户端浏览器)

    wk.write();

    //操作完成时,关闭对象,释放占用的内存空间

    wk.close();
      加下划线这部分代码是B/S模式中采用的输出方式,而不是输出到本地指定的磁盘目录。该代码表示将temp.xls的Excel文件通过应答实体(response)输出给请求的客户端浏览器,下载到客户端本地(保存或直接打开)。若要直接输出到磁盘文件可采用下列代码替换加下划线这部分代码
    File file=new File(“D://temp.xls”);
    WritableWorkbook wwb = Workbook.createWorkbook(file);

    3.3高级操作

    3.3.1数据格式化
    在Excel中不涉及复杂的数据类型,能够比较好的处理字串、数字和日期已经能够满足一般的应用即可。

    数据的格式化涉及到的是字体、粗细、字号等元素,这些功能主要由 WritableFont和WritableCellFormat类来负责。例如:

    ① WritableFont font=new WritableFont(WritableFont.createFont(“宋体”),12,WritableFont.NO_BOLD );

    ② WritableCellFormat format1=new WritableCellFormat(font);

    ③ Label label=new Label(0,0,”data 4 test”,format1);

    其中

    I.指定了字串格式:字体为宋体,字号16,加粗显示。WritableFont有非常丰富的构造子,供不同情况下使用,jExcelAPI的java-doc中有详细列表,这里不再列出。

    II. 处代码使用了WritableCellFormat类,这个类非常重要,通过它可以指定单元格的各种属性,如上例代码所示。

    III. 处使用了Label类的构造子,指定了显示的位置,文本内容,字串被赋予的格式。

    与Label类似的Number、DateTime,区别Label表示文本数据;Number表示数值数据,可使NumberFormat格式化数据;用DateTime表示日期型数据,可应用DateFormat格式化数据。

    3.3.2单元格操作
    Excel中很重要的一部分是对单元格的操作,比如行高、列宽、单元格合并等,所幸jExcelAPI提供了这些支持。这些操作相对比较简单,下面只介绍一下相关的API。

    1、 合并单元格

    [java] view plain copy print?
    WritableSheet.mergeCells(int m,int n,int p,int q);

    //作用是从(m,n)到(p,q)的单元格全部合并,比如:

    WritableSheet sheet=book.createSheet(“第一页”,0);

    //合并第一列第一行到第六列第一行的所有单元格

    sheet.mergeCells(0,0,5,0);

    //合并既可以是横向的,也可以是纵向的。合并后的单元格不能再次进行合并,否则会触发异常。

    2、 行高和列宽

    [java] view plain copy print?
    writableSheet.setRowView(int i,int height);

    //作用是指定第i+1行的高度,比如:

    // 将第一行的高度设为200

    sheet.setRowView(0,200);

    WritableSheet.setColumnView(int i,int width);

    //作用是指定第i+1列的宽度,比如:

    //将第一列的宽度设为30

    sheet.setColumnView(0,30);

    3.4  从Excel文件读取数据表

    我们就以导入3.2所产生的excel表为例,掌握如何编写导入Excel代码(该代码封装在业务层方法)

    [java] view plain copy print?
    public List loadScoreInfo(String xlsPath) throws IOException, BiffException{

    //导入已存在的Excel文件,获得只读的工作薄对象
    FileInputStream fis=new FileInputStream(xlsPath);
    Workbook wk=Workbook.getWorkbook(fis);
    //获取第一张Sheet表
    Sheet sheet=wk.getSheet(0);
    //获取总行数
    int rowNum=sheet.getRows();
    //从数据行开始迭代每一行
    for(int i=2;i

    展开全文
  • 我基于自己做项目时的一些需求,针对POI进行了二次封装,并进行了一系列改进,使Java操作Excel、Word等文件变得更加简单。本框架相比于hutool等更“轻”,没有十分复杂的设计模式、接口等,上手更加容易。 [toc] ...
  • 在程序项目开发中,无论是Android还是Java,在很多地方都会使用到Excle表格作为数据的存储方式,这就避免不了程序对Excle文件的操作。 问题:目前,无论是JXL还是POI,对Excel操作都是比较原始的,用户无法使用...
  • 主要介绍了Java基础开发之数据导出Excel文件格式实例详解,需要的朋友可以参考下
  • 使用jxls框架生成Excel,简单易懂。内含所使用的引用jar包版本。
  • 目录 背景描述 技术准备 导出Excel——尝鲜版 导出Excel——封装版(通过反射) 导出Excel——深度封装(设置下拉选项...最近博主在做的Web项目中,有一个导出数据到Excel表格的需求,之前纯JS实现过,这次打算...

    目录

    背景描述

    技术准备

    导出Excel——尝鲜版

    导出Excel——封装版(通过反射)

    导出Excel——深度封装(设置下拉选项)

     扩展——多个列分别是不同的下拉选项怎么封装

    2019-10-28  更新,必看!!!

    2019-12-18更新,修复小概率的文件名乱码问题


    背景描述

    最近博主在做的Web项目中,有一个导出数据到Excel表格的需求,之前用纯JS实现过,这次打算用Java在后端实现,将数据通过response以IO流的方式传输给前端,使浏览器能直接下载。这里做一下记录、笔记。

    技术准备

    我的项目是基于Spring Boot的,这里只贴出POI框架需要依赖的两个包,其他的都无所谓,只要能提供Controller让浏览器访问即可

    <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi</artifactId>
                <version>3.14</version>
            </dependency>
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi-ooxml</artifactId>
                <version>3.14</version>
            </dependency>

    导出Excel——尝鲜版

    这里为了让大家理解POI这个框架一系列的API,先以最low的方式去实现,稍后我们再进行封装,达到“编写一次、处处可用”。

    我们只需要提供一个Controller接口:

    
        /**
         *  导出数据到Excel
         * @param response 响应体
         * 注意,我这里是基于Spring Boot的,全局有一个@RestController注解,所以没加@ResponseBody,
         * 如果你的不是,请加上@ResponseBody注解
         * */
        @GetMapping(value = "/out-excel-demo")
        public Object outExcelDemo(HttpServletResponse response) throws IOException {
            //创建HSSFWorkbook对象(excel的文档对象)
            HSSFWorkbook wb = new HSSFWorkbook();
            //创建sheet对象(excel的表单)
            HSSFSheet sheet=wb.createSheet("sheet1");
    
            //创建第一行,这里即是表头。行的最小值是0,代表每一行,上限没研究过,可参考官方的文档
            HSSFRow row1=sheet.createRow(0);
            //在这一行创建单元格,并且将这个单元格的内容设为“账号”,下面同理。
            //列的最小值标识也是0
            row1.createCell(0).setCellValue("账号");
            row1.createCell(1).setCellValue("用户名");
            row1.createCell(2).setCellValue("日期");
            row1.createCell(3).setCellValue("是否完成");
    
            //第二行
            HSSFRow row2=sheet.createRow(1);
            row2.createCell(0).setCellValue("123456");
            row2.createCell(1).setCellValue("张三");
            row2.createCell(2).setCellValue("2019-08-05");
            row2.createCell(3).setCellValue("是");
    
            //第三行
            HSSFRow row3=sheet.createRow(2);
            row3.createCell(0).setCellValue("5681464");
            row3.createCell(1).setCellValue("李四");
            row3.createCell(2).setCellValue("2019-08-01");
            row3.createCell(3).setCellValue("否");
    
            //输出Excel文件
            OutputStream output=response.getOutputStream();
            response.reset();
            response
                    .setHeader("Content-disposition", "attachment; filename=demo.xls");
            response.setContentType("application/x-xls");
            wb.write(output);
            output.close();
            return null;
        }
    

    然后你可以在页面上写一个按钮,点击的时候通过location.href指向上面的接口路径,我这里就省略了,看一下效果:

    打开表格:

     到这里,相信大家对POI有认识了吧?

    其实它就是以每个HSSFRow为一个主体,每一个HSSFRow代表一行记录,我们只需要通过这个对象的createCell方法去创建单元格、赋值就行,这样就很清晰了吧?

    导出Excel——封装版(通过反射)

    以上我们实现了简单的数据导出,但是实际的场景根本不是这样,我们都是从数据库里查出来数据,而且不可能这样一行一行的去设置。

    你肯定想到了循环,没错,循环是肯定的,但是仅仅循环还不够灵活,为什么呢?

    根据面向对象的思维,我们可以将所有的表头(即第一行)做成一个List集合参数,将所有的数据做成一个List集合参数,这个数据集合的泛型是我们的POJO实体类,然后我们两个循环就能省略一大段代码。

    但是问题来了,我们例子中的导出表格,是“账号、用户名、日期、是否完成”这四个表头,实体类也是对应的四个属性。假如又来了一个导出需求呢?表头不一样了,所对应的实体类也不一样了,难道我们再封装成一个其他的方法?难道每个不同的Excel表结构都要封装一个新的方法吗?

    做的时候博主立马就想到了反射机制,我们可以传入List集合,对泛型不做限制,遍历数据集合的时候,通过反射得到这个对象的字段,动态赋值。

    但是这就有一个强制要求:在实体类声明字段的时候,顺序必须和表头的前后顺序一致,否则循环遍历的时候会出现数据不对应的现象

    首先我们声明一个实体类,这也符合我们真正的开发环境:

    package com.dosion.smart.future.api.entity.activity.json;
    
    import lombok.Data;
    
    /**
     *  导出报名情况的数据传输对象
     * @author 秋枫艳梦
     * @date 2019-08-05
     * */
    @Data
    public class SignOutExcelJSON {
        //用户账号
        private String account;
        //用户名
        private String username;
        //报名时间
        private String signDate;
        //是否完成
        private String finish;
    }
    

    然后封装一个工具类出来:

    package com.dosion.smart.future.utils;
    
    import com.dosion.smart.future.api.entity.activity.json.SignOutExcelJSON;
    import org.apache.poi.hssf.usermodel.HSSFCell;
    import org.apache.poi.hssf.usermodel.HSSFRow;
    import org.apache.poi.hssf.usermodel.HSSFSheet;
    import org.apache.poi.hssf.usermodel.HSSFWorkbook;
    import org.apache.poi.ss.util.CellRangeAddress;
    
    import java.lang.reflect.Field;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    
    /**
     *  数据导出excel,工具类
     * @author 秋枫艳梦
     * @date 209-08-05
     * */
    public class ExcelUtil {
    
        /**
         *  生成Excel表格
         * @param sheetName sheet名称
         * @param titleList 表头列表
         * @param dataList 数据列表
         * @return HSSFWorkbook对象
         * */
        public static HSSFWorkbook createExcel(String sheetName,
                                               List<String> titleList,List dataList) throws IllegalAccessException {
    
            //创建HSSFWorkbook对象
            HSSFWorkbook wb = new HSSFWorkbook();
            //创建sheet对象
            HSSFSheet sheet=wb.createSheet(sheetName);
            //在sheet里创建第一行,这里即是表头
            HSSFRow rowTitle=sheet.createRow(0);
    
            //写入表头的每一个列
            for (int i = 0; i < titleList.size(); i++) {
                //创建单元格
                rowTitle.createCell(i).setCellValue(titleList.get(i));
            }
    
            //写入每一行的记录
            for (int i = 0; i < dataList.size(); i++) {
                //创建新的一行,递增
                HSSFRow rowData = sheet.createRow(i+1);
    
                //通过反射,获取POJO对象
                Class cl = dataList.get(i).getClass();
                //获取类的所有字段
                Field[] fields = cl.getDeclaredFields();
                for (int j = 0; j < fields.length; j++) {
                    //设置字段可见,否则会报错,禁止访问
                    fields[j].setAccessible(true);
                    //创建单元格
                    rowData.createCell(j).setCellValue((String) fields[j].get(dataList.get(i)));
                }
            }
            return wb;
        }
    }
    

    然后我们模仿一下调用(这里手动制造数据,真实情况下通过数据库查询):

    /**
         *  导出Excel
         * 
         * 
         * */
        @GetMapping(value = "/out-excel-demo")
        public String outExcelDemo(HttpServletResponse response) throws IOException, IllegalAccessException {
            //文件名
            String fileName = "活动报名情况一览表";
            //sheet名
            String sheetName = "报名情况sheet";
    
            //表头集合,作为表头参数
            List<String> titleList = new ArrayList<>();
            titleList.add("用户账户");
            titleList.add("用户名");
            titleList.add("报名时间");
            titleList.add("是否完成");
    
            //数据对象,这里模拟手动添加,真实的环境往往是从数据库中得到
            SignOutExcelJSON excelJSON = new SignOutExcelJSON();
            excelJSON.setAccount("18210825916");
            excelJSON.setUsername("张三");
            excelJSON.setSignDate("2019-08-05");
            excelJSON.setFinish("是");
            SignOutExcelJSON excelJSON2 = new SignOutExcelJSON();
            excelJSON2.setAccount("15939305781");
            excelJSON2.setUsername("李四");
            excelJSON2.setSignDate("2019-08-01");
            excelJSON2.setFinish("否");
            //将两个对象加入到集合中,作为数据参数
            List<SignOutExcelJSON> excelJSONList = new ArrayList<>();
            excelJSONList.add(excelJSON);
            excelJSONList.add(excelJSON2);
    
            //调取封装的方法,传入相应的参数
            HSSFWorkbook workbook = ExcelUtil.createExcel(sheetName,titleList, excelJSONList);
    
            //输出Excel文件
            OutputStream output=response.getOutputStream();
            response.reset();
            //中文名称要进行编码处理
            response
                    .setHeader("Content-disposition", "attachment; filename="+new String(fileName.getBytes("GB2312"),"ISO8859-1")+".xls");
            response.setContentType("application/x-xls");
            workbook.write(output);
            output.close();
            return null;
        }

    运行结果:

     

     效果是一样的,而且很灵活。各位可以试一下,假如你导出其他模块的数据,你只需要传入不同的表头集合、实体类集合,就能实现你的需求,这也是封装的魅力所在。

    导出Excel——深度封装(设置下拉选项)

    假如我有一个需求:是否完成这一列只能输入是否,以下拉框的形式出现。

    POI框架肯定有对应的API,大家看文档也能学会,这里我带大家封装一下,毕竟以可重用性为荣。

    先封装一个下拉条件对象:

    package com.dosion.smart.future.api.entity.activity;
    
    import lombok.Data;
    
    /**
     *  导出Excel时的条件,有下列选项时使用
     * @author 秋枫艳梦
     * @date 2019-08-05
     * */
    @Data
    public class OutExcelQuery {
        //起始行
        private int rowStart;
        //结束行
        private int rowEnd;
        //起始列
        private int colStart;
        //结束列
        private int colEnd;
        //下拉参数
        private String[] params;
    
        //构造函数
        public OutExcelQuery(int rowStart,int rowEnd,int colStart,int colEnd,String[] params){
            this.rowStart = rowStart;
            this.rowEnd = rowEnd;
            this.colStart = colStart;
            this.colEnd = colEnd;
            this.params = params;
        }
    }
    

    再贴出来工具类:

    package com.dosion.smart.future.utils;
    
    import com.dosion.smart.future.api.entity.activity.OutExcelQuery;
    import com.dosion.smart.future.api.entity.activity.json.SignOutExcelJSON;
    import org.apache.poi.hssf.usermodel.*;
    import org.apache.poi.ss.util.CellRangeAddress;
    import org.apache.poi.ss.util.CellRangeAddressList;
    
    import java.lang.reflect.Field;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    
    /**
     *  数据导出excel,工具类
     * @author 秋枫艳梦
     * @date 209-08-05
     * */
    public class ExcelUtil {
    
        /**
         *  生成Excel表格
         * @param sheetName sheet名称
         * @param titleList 表头列表
         * @param dataList 数据列表
         * @param outExcelQuery 下拉选项设置
         * @return HSSFWorkbook对象
         * */
        public static HSSFWorkbook createExcel(String sheetName, List<String> titleList,
                                               List dataList, OutExcelQuery outExcelQuery) throws IllegalAccessException {
    
            //创建HSSFWorkbook对象(excel的文档对象)
            HSSFWorkbook wb = new HSSFWorkbook();
            //创建sheet对象(excel的表单)
            HSSFSheet sheet=wb.createSheet(sheetName);
            //在sheet里创建第一行,这里即是表头
            HSSFRow rowTitle=sheet.createRow(0);
    
            //写入表头的每一个列
            for (int i = 0; i < titleList.size(); i++) {
                //创建单元格
                rowTitle.createCell(i).setCellValue(titleList.get(i));
            }
    
            //写入每一行的记录
            int count = 0;
            for (int i = 0; i < dataList.size(); i++) {
                count++;
                //创建新的一行,递增
                HSSFRow rowData = sheet.createRow(i+1);
    
                //通过反射,获取POJO对象
                Class cl = dataList.get(i).getClass();
                //获取类的所有字段
                Field[] fields = cl.getDeclaredFields();
                for (int j = 0; j < fields.length; j++) {
                    //设置字段可见,否则会报错,禁止访问
                    fields[j].setAccessible(true);
                    //创建单元格
                    rowData.createCell(j).setCellValue((String) fields[j].get(dataList.get(i)));
                }
            }
    
            //如果开启了下拉选项
            if (outExcelQuery!=null){
                //如果表格中的记录数不是0
                if (count!=0){
                    // 获取下拉列表数据
                    String[] strs = outExcelQuery.getParams();
                    //设置哪些行的哪些列为下拉选项
                    CellRangeAddressList rangeList =
                            new CellRangeAddressList(outExcelQuery.getRowStart(),
                                    //结束行为-1时,说明设置所有行
                                    outExcelQuery.getRowEnd()==-1?count:outExcelQuery.getRowEnd(),
                                    outExcelQuery.getColStart(),outExcelQuery.getColEnd());
                    //绑定下拉数据
                    DVConstraint constraint = DVConstraint.createExplicitListConstraint(strs);
                    //绑定两者的关系
                    HSSFDataValidation dataValidation = new HSSFDataValidation(rangeList,constraint);
                    //添加到sheet中
                    sheet.addValidationData(dataValidation);
                }
            }
            return wb;
        }
    }
    

     如果我不想设置任何列为下拉选项,那我调用的时候将最后一个参数传入null即可。如果想设置某一列或某几列为下拉选项,那我调用的时候只需要这样(省略其他代码):

    String[] params = new String[]{"是","否"};
    //从第一行开始,到最后一行结束,设置第4列为下拉选项
    OutExcelQuery outExcelQuery = new OutExcelQuery(1,-1,3,3,params);
    HSSFWorkbook workbook = ExcelUtil.createExcel(sheetName,titleList,activityService.outExcel(id),outExcelQuery);

     效果:

     扩展——多个列分别是不同的下拉选项怎么封装

    以上下拉选项的封装,只是针对某一列或某几列使用相同的下拉选项的情况,假如几个数据列的下拉选项不同呢?

    比如,再加一个性别列,下拉选项的值是男和女,此时一张Excel表中就出现了两个下拉选项设置,该怎么封装?

    博主就不再写了,留给大家思考,有疑问的可以留言。

    提示一个思路:可以应用Java可选参数的特性,传入多个OutExcelQuery对象,进行循环添加条件

    挖坑填坑,其乐融融。

     

    2019-10-28  更新,必看!!!

    最近有一位博友用到了我的这个工具类,首先表示很荣幸。

    但是帮他解决问题的过程中,也发现了这个工具类的一点瑕疵,那就是:

    之前的版本,必须要求表头、实体类的字段一一对应,且顺序要一致,比如你导出的表格中有姓名和年龄两个列,那么你的实体类中只能有name和age两个字段,且顺序要一致,否则会出现年龄的值出现在姓名列的情况。

    这样确实有点不灵活,如果一个实体类有多个字段呢?如果依然采用这种方式,那我岂不是还要为导出表格专门写一个实体类?

    所以,我做了以下改进,在循环写入每一行的列的时候,遍历的是titleList集合的长度,而不是实体类的字段数量,这样一来,我们有一个表头列,就会遍历出对象的几个属性,对象其他的属性将不会体现到表格里。举个例子:

    假设导出的表头有姓名、年龄两个列,但是实体类有name、age、sex三个字段,那么我们遍历的是表头的长度,即2,那么sex这个字段将不会被写入表格里。这样一来,实体类就可以有任意个字段了,只需要保证前n个字段与表头保持一致即可。

    需要注意的是,你需要导出的字段,在实体类里仍然需要按照表头的顺序进行排列,没办法,只能这么取舍了,否则就做不到万能了,当然大家也可以根据自己的业务去做定制化。

    另外,此次增加了Excel的导入功能。

    最后贴出来完整的工具类:

    import com.dosion.model.activity.query.OutExcelQuery;
    import org.apache.poi.hssf.usermodel.*;
    import org.apache.poi.ss.usermodel.Cell;
    import org.apache.poi.ss.usermodel.Row;
    import org.apache.poi.ss.usermodel.Sheet;
    import org.apache.poi.ss.usermodel.Workbook;
    import org.apache.poi.ss.util.CellRangeAddress;
    import org.apache.poi.ss.util.CellRangeAddressList;
    import org.apache.poi.xssf.usermodel.XSSFWorkbook;
    import org.springframework.web.multipart.MultipartFile;
     
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    import java.lang.reflect.Field;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
     
    /**
     *  数据导出、导入excel,工具类
     * @author 秋枫艳梦
     * @date 2019-10-28
     * */
    public class ExcelUtil {
     
        /**
         *  生成Excel表格
         * @param sheetName sheet名称
         * @param titleList 表头列表
         * @param dataList 数据列表
         * @param outExcelQuery 下拉选项设置
         * @return HSSFWorkbook对象
         * */
        public static HSSFWorkbook createExcel(String sheetName, List<String> titleList,
                                               List dataList, OutExcelQuery outExcelQuery) throws IllegalAccessException {
     
            //创建HSSFWorkbook对象(excel的文档对象)
            HSSFWorkbook wb = new HSSFWorkbook();
            //创建sheet对象(excel的表单)
            HSSFSheet sheet=wb.createSheet(sheetName);
            //在sheet里创建第一行,这里即是表头
            HSSFRow rowTitle=sheet.createRow(0);
     
            //写入表头的每一个列
            for (int i = 0; i < titleList.size(); i++) {
                //创建单元格
                rowTitle.createCell(i).setCellValue(titleList.get(i));
            }
     
            int count = 0;
            //写入每一行的记录
            if (dataList!=null){
                for (int i = 0; i < dataList.size(); i++) {
                    count++;
                    //创建新的一行,递增
                    HSSFRow rowData = sheet.createRow(i+1);
     
                    //通过反射,获取POJO对象
                    Class cl = dataList.get(i).getClass();
                    //获取类的所有字段
                    Field[] fields = cl.getDeclaredFields();
                    for (int j = 0; j < titleList.size(); j++) {
                        //设置字段可见,否则会报错,禁止访问
                        fields[j].setAccessible(true);
                        //创建单元格
                        rowData.createCell(j).setCellValue((String) fields[j].get(dataList.get(i)));
                    }
                }
            }
     
            //如果开启了下拉选项
            if (outExcelQuery!=null){
                //如果表格中的记录数不是0
                if (count!=0){
                    // 获取下拉列表数据
                    String[] strs = outExcelQuery.getParams();
                    //设置哪些行的哪些列为下拉选项
                    CellRangeAddressList rangeList =
                            new CellRangeAddressList(outExcelQuery.getRowStart(),
                                    //结束行为-1时,说明设置所有行
                                    outExcelQuery.getRowEnd()==-1?count:outExcelQuery.getRowEnd(),
                                    outExcelQuery.getColStart(),outExcelQuery.getColEnd());
                    //绑定下拉数据
                    DVConstraint constraint = DVConstraint.createExplicitListConstraint(strs);
                    //绑定两者的关系
                    HSSFDataValidation dataValidation = new HSSFDataValidation(rangeList,constraint);
                    //添加到sheet中
                    sheet.addValidationData(dataValidation);
                }
            }
            return wb;
        }
     
        /**
         * 读入excel文件,解析后返回
         * @param file
         * @throws IOException
         */
        public static List<String[]> readExcel(MultipartFile file) throws IOException{
            //检查文件
            checkFile(file);
            //获得Workbook工作薄对象
            Workbook workbook = getWorkBook(file);
            //创建返回对象,把每行中的值作为一个数组,所有行作为一个集合返回
            List<String[]> list = new ArrayList<String[]>();
            if(workbook != null){
                for(int sheetNum = 0;sheetNum < workbook.getNumberOfSheets();sheetNum++){
                    //获得当前sheet工作表
                    Sheet sheet = workbook.getSheetAt(sheetNum);
                    if(sheet == null){
                        continue;
                    }
                    //获得当前sheet的开始行
                    int firstRowNum  = sheet.getFirstRowNum();
                    //获得当前sheet的结束行
                    int lastRowNum = sheet.getLastRowNum();
                    //循环除了第一行的所有行
                    for(int rowNum = firstRowNum+1;rowNum <= lastRowNum;rowNum++){
                        //获得当前行
                        Row row = sheet.getRow(rowNum);
                        if(row == null){
                            continue;
                        }
                        //获得当前行的开始列
                        int firstCellNum = row.getFirstCellNum();
                        //获得当前行的列数
                        int lastCellNum = row.getPhysicalNumberOfCells();
                        String[] cells = new String[row.getPhysicalNumberOfCells()];
                        //循环当前行
                        for(int cellNum = firstCellNum; cellNum < lastCellNum;cellNum++){
                            Cell cell = row.getCell(cellNum);
                            cells[cellNum] = getCellValue(cell);
                        }
                        list.add(cells);
                    }
                }
                workbook.close();
            }
            return list;
        }
     
        /**
         * 检查用户上传的文件
         * @param file 文件对象
         * */
        private static void checkFile(MultipartFile file) throws IOException{
            //判断文件是否存在
            if(null == file){
                throw new FileNotFoundException("文件不存在!");
            }
            //获得文件名
            String fileName = file.getOriginalFilename();
            //判断文件是否是excel文件
            if(!fileName.endsWith("xls") && !fileName.endsWith("xlsx")){
                throw new IOException(fileName + "不是excel文件");
            }
        }
     
        /**
         *  获取Workbook对象
         * @param file 文件对象
         * @return Workbook对象
         * */
        private static Workbook getWorkBook(MultipartFile file) {
            //获得文件名
            String fileName = file.getOriginalFilename();
            //创建Workbook工作薄对象,表示整个excel
            Workbook workbook = null;
            try {
                //获取excel文件的io流
                InputStream is = file.getInputStream();
                //根据文件后缀名不同(xls和xlsx)获得不同的Workbook实现类对象
                if(fileName.endsWith("xls")){
                    //2003
                    workbook = new HSSFWorkbook(is);
                }else if(fileName.endsWith("xlsx")){
                    //2007
                    workbook = new XSSFWorkbook(is);
                }
            } catch (IOException e) {
     
            }
            return workbook;
        }
     
        /**
         *  获取单元格的值
         * @param cell 单元格对象
         * @return 值
         * */
        private static String getCellValue(Cell cell){
            String cellValue = "";
            if(cell == null){
                return cellValue;
            }
            //把数字当成String来读,避免出现1读成1.0的情况
            if(cell.getCellType() == Cell.CELL_TYPE_NUMERIC){
                cell.setCellType(Cell.CELL_TYPE_STRING);
            }
            //判断数据的类型
            switch (cell.getCellType()){
                case Cell.CELL_TYPE_NUMERIC: //数字
                    cellValue = String.valueOf(cell.getNumericCellValue());
                    break;
                case Cell.CELL_TYPE_STRING: //字符串
                    cellValue = String.valueOf(cell.getStringCellValue());
                    break;
                case Cell.CELL_TYPE_BOOLEAN: //Boolean
                    cellValue = String.valueOf(cell.getBooleanCellValue());
                    break;
                case Cell.CELL_TYPE_FORMULA: //公式
                    cellValue = String.valueOf(cell.getCellFormula());
                    break;
                case Cell.CELL_TYPE_BLANK: //空值
                    cellValue = "未填写";
                    break;
                case Cell.CELL_TYPE_ERROR: //故障
                    cellValue = "非法字符";
                    break;
                default:
                    cellValue = "未知类型";
                    break;
            }
            return cellValue;
        }
    }

    2019-12-18更新,修复小概率的文件名乱码问题

    最近有朋友反映说,他导出时有时候会出现文件名乱码的问题,博主复现了很多次,都没有复现出来。后来同事也反映这个问题,博主才重视起来。

    经排查发现,首先之前的ISO8859-1,博主写的不规范,应该是ISO-8859-1(但是我感觉跟它没关系,哈哈);其次,博主把响应流的类型设置为了application/x-xls,这是标准的excel响应流格式。亲测有效,无论是.xls后缀还是.xlsx后缀,都没有问题。

    在这里要感谢这篇文章,总结了各种的response响应流格式:

    https://blog.csdn.net/luman1991/article/details/53423305

    贴出关键代码部分:

    //输出Excel文件
            OutputStream output=response.getOutputStream();
            response.reset();
            //中文名称要进行编码处理
            response
                    .setHeader("Content-disposition", "attachment; filename="+new String("游戏列表".getBytes("GB2312"),"ISO-8859-1")+".xlsx");
            response.setContentType("application/x-xls");
            workbook.write(output);
            output.close();

    另外,我发现有一个奇怪的现象,大部分中文名都能兼容,但是我把“游戏列表”换成“学校”,那么就会乱码,换成“学校列表”就又好了……

    有人说跟浏览器的编码方式有关,需要在后端代码做处理,但是博主试了一个遍,还是失败。。。博主就退而求其次吧,我觉得这是可以接受的,换一个同义的文件名而已。

    展开全文
  • 视频链接-我是学习之星我为狂神打call~ 【狂神说Java】POI及EasyExcel一小时搞定通俗易懂 想给项目添加一个表格导入导出...这篇文章简单介绍下如何用java操作excel,主要涉及到POI和easyExcel这两个 文章目录POI和e...

    视频链接-我是学习之星我为狂神打call~
    【狂神说Java】POI及EasyExcel一小时搞定通俗易懂


    想给项目添加一个表格导入导出功能吗?
    “xxx管理系统”没有导入导出功能逼格不够了?
    想简单入手下 how to 用java 生成excel又找不到合适的教程?

    come on !这篇文章简单介绍下如何用java操作excel,主要涉及到POIeasyExcel这两个

    1.POI和easyExcel介绍

    是什么?

    我们经常需要将项目中的表格数据或者文档数据进行导入或者导出操作,这个如果自己从零开始做还比较麻烦。比如我之前就职的公司都是自己做的组件,但是很不好用,BUG 太多。关于表格导入导出,市面上比较知名的开源就是 Apache 的POI 和 阿里巴巴的 EasyExcel了。EasyExcel 也是对 POI 的改进和封装, 更加好用。

    有什么用?

    1、将用户的信息导出为 excel 表格。

    2、将 Excel 表中的信息录入到网站数据库。

    开发中经常会涉及到 excel 的 处理,如导出 Excel ,导入 Excel 到数据库中。

    操作 Excel 目前比较流行的就是 Apache POI 和阿里巴巴的 EasyExcel。

    Apache POI
    结构:
    在这里插入图片描述

    EasyExcel
    EasyExcel 是阿里巴巴开源的一个 excel处理框架,以使用简单、节省内存著称。

    EasyExcel 能大大减少内存占用的主要原因是在解析 Excel 时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。

    下面是 EasyExcel 和 POI 在解析Excel 时的对比图。
    在这里插入图片描述

    2.POI-excel-写

    excel 基本写入操作

    在这里插入图片描述
    万物皆对象:工作簿是个对象,其中包含工作表;工作表是个对象,其中包含行,列对象。。
    在这里插入图片描述

    03版本xls

    	@Test
        public void testWriteTest03() throws Exception {
    
            // 1.创建一个工作簿
            Workbook workbook =new HSSFWorkbook();
            // 2.创建一个工作表
            Sheet sheet = workbook.createSheet("学生统计表");
            // 3.创建一个行(1,1)
            Row row1 =sheet.createRow(0);
            // 4.创建一个单元格 (1,2)
            Cell cell11 =row1.createCell(0);
            cell11.setCellValue("学生姓名");
    
            Cell cell12 =row1.createCell(1);
            cell12.setCellValue("杨涛");
    
            //第二行
            Row row2 =sheet.createRow(1);
            Cell cell21 =row2.createCell(0);
            cell21.setCellValue("入学时间");
            //( 2,2)
            Cell cell22 =row2.createCell(1);
            String time = new DateTime().toString("yyyy-MM-dd");
            cell22.setCellValue(time);
    
            // 生成一张表(IO流) 03 版本使用的就是 xls结尾!
            FileOutputStream fileOutputStream = new FileOutputStream(PATH + "学生统计表03.xls");
            workbook.write(fileOutputStream);
    
            // 关闭流
            fileOutputStream.close();
            System.out.println("学生统计表03表格生成完毕");
        }
    

    07版本xlsx

    	@Test
        public void testWriteTest07() throws Exception {
    
            // 1.创建一个工作簿 07
            Workbook workbook =new XSSFWorkbook();
            // 2.创建一个工作表
            Sheet sheet = workbook.createSheet("学生统计表");
            // 3.创建一个行(1,1)
            Row row1 =sheet.createRow(0);
            // 4.创建一个单元格 (1,2)
            Cell cell11 =row1.createCell(0);
            cell11.setCellValue("学生姓名");
    
            Cell cell12 =row1.createCell(1);
            cell12.setCellValue("杨涛");
    
            //第二行
            Row row2 =sheet.createRow(1);
            Cell cell21 =row2.createCell(0);
            cell21.setCellValue("入学时间");
            //( 2,2)
            Cell cell22 =row2.createCell(1);
            String time = new DateTime().toString("yyyy-MM-dd");
            cell22.setCellValue(time);
    
            // 生成一张表(IO流) 03 版本使用的就是 xlsx结尾!
            FileOutputStream fileOutputStream = new FileOutputStream(PATH + "学生统计表07.xlsx");
            workbook.write(fileOutputStream);
    
            // 关闭流
            fileOutputStream.close();
            System.out.println("学生统计表07表格生成完毕");
        }
    

    大量数据写入

    PS:xls文件有行数限制(65536行)。
    03版本xls对应的是HSSF格式,07版本xlsx对应的是XSSF格式
    注意:03版本xls写的行数超过65536会报错异常:

    java.lang.IllegalArgumentException: Invalid row number (65536) outside allowable range (0..65535)
    

    xlsx

    大文件写HSSF

    @Test
        public void testWrite03BigData() throws Exception {
    
            // 时间
            long begin = System.currentTimeMillis();
            // 创建一个簿
            Workbook workbook = new HSSFWorkbook();
            //创建表
            Sheet sheet = workbook.createSheet();
            //写入数据
            for (int rowNum = 0; rowNum < 65535; rowNum++) {
                Row row = sheet.createRow(rowNum);
                for (int cellNum = 0; cellNum < 10; cellNum++) {
                    Cell cell = row.createCell(cellNum);
                    cell.setCellValue(cellNum);
                }
            }
            System.out.println("over");
    
            // 输出文件
            FileOutputStream outputStream = new FileOutputStream(PATH + "testWrite03BigData.xls");
            workbook.write(outputStream);
            // 关闭流
            outputStream.close();
    
            long end = System.currentTimeMillis();
            System.out.println((double)(end-begin)/1000);
        }
    

    大文件写XSSF

    // 耗时较久!!
        @Test
        public void testWrite07BigData() throws Exception {
    
            // 时间
            long begin = System.currentTimeMillis();
            // 创建一个簿
            Workbook workbook = new XSSFWorkbook();
            //创建表
            Sheet sheet = workbook.createSheet();
            //写入数据
            for (int rowNum = 0; rowNum < 200000; rowNum++) {
                Row row = sheet.createRow(rowNum);
                for (int cellNum = 0; cellNum < 10; cellNum++) {
                    Cell cell = row.createCell(cellNum);
                    cell.setCellValue(cellNum);
                }
            }
            System.out.println("over");
    
            // 输出文件
            FileOutputStream outputStream = new FileOutputStream(PATH + "testWrite07BigData.xlsx");
            workbook.write(outputStream);
            // 关闭流
            outputStream.close();
    
            long end = System.currentTimeMillis();
            System.out.println((double)(end-begin)/1000);
        }
    

    大文件写SXSSF

    在这里插入图片描述

    	@Test
        public void testWrite07BigDataSecond() throws Exception {
    
            // 时间
            long begin = System.currentTimeMillis();
            // 创建一个簿
            Workbook workbook = new SXSSFWorkbook();
            //创建表
            Sheet sheet = workbook.createSheet();
            //写入数据
            for (int rowNum = 0; rowNum < 200000; rowNum++) {
                Row row = sheet.createRow(rowNum);
                for (int cellNum = 0; cellNum < 10; cellNum++) {
                    Cell cell = row.createCell(cellNum);
                    cell.setCellValue(cellNum);
                }
            }
            System.out.println("over");
    
            // 输出文件
            FileOutputStream outputStream = new FileOutputStream(PATH + "testWrite07BigDataSecond.xlsx");
            workbook.write(outputStream);
            // 关闭流
            outputStream.close();
    
            // 清除临时文件!
            ((SXSSFWorkbook)workbook).dispose();
    
            long end = System.currentTimeMillis();
            System.out.println((double)(end-begin)/1000);
        }
    

    3.POI-excel-读

    03/07版本

    03版本代码示例

        String PATH = "C:\\Users\\Administrator\\Desktop\\poi-easyExcel\\";
    
        @Test
        public void testReadTest03() throws Exception {
    
            // 获取文件流
            FileInputStream inputStream = new FileInputStream(PATH + "poi学生统计表03.xls");
    
            // 1.创建一个工作簿, excel 能操作的它都能操作
            Workbook workbook = new HSSFWorkbook(inputStream);
            // 2.得到表
            Sheet sheet = workbook.getSheetAt(0);
            // 3.得到行
            Row row = sheet.getRow(0);
            // 4.得到列
            Cell cell = row.getCell(1);
    
            // 输出(0,0)
            // 读取值的时候一定要注意判断类型,否则会读取失败
            System.out.println(cell.getStringCellValue());
    
        }
    

    07版本代码示例

        @Test
        public void testReadTest07() throws Exception {
    
            // 获取文件流
            FileInputStream inputStream = new FileInputStream(PATH + "poi学生统计表07.xlsx");
    
            // 1.创建一个工作簿, excel 能操作的它都能操作
            Workbook workbook = new XSSFWorkbook(inputStream);
            // 2.得到表
            Sheet sheet = workbook.getSheetAt(0);
            // 3.得到行
            Row row = sheet.getRow(0);
            // 4.得到列
            Cell cell = row.getCell(1);
    
            // 输出(0,0)
            // 读取值的时候一定要注意判断类型,否则会读取失败
            System.out.println(cell.getStringCellValue());
    
        }
    

    注意获取值的类型即可

    读取不同类型的数据(难点★)

        @Test
        public void testCellType() throws Exception {
            // 获取文件流
            FileInputStream inputStream = new FileInputStream(PATH + "明细表.xls");
    
            // 1.创建一个工作簿, excel 能操作的它都能操作
            Workbook workbook = new HSSFWorkbook(inputStream);
            Sheet sheet = workbook.getSheetAt(0);
            // 获取标题内容
            Row rowTitle = sheet.getRow(0);
            if (rowTitle != null) {
                // 一定要掌握!!
                int cellCount = rowTitle.getPhysicalNumberOfCells();
                for (int cellNum = 0; cellNum < cellCount; cellNum++) {
                    Cell cell = rowTitle.getCell(cellNum);
                    if (cell != null) {
                        int cellType = cell.getCellType();
                        String cellValue = cell.getStringCellValue();
                        System.out.print(cellValue + " | ");
                    }
                }
                System.out.println();
            }
    
            // 获取表中的内容
            int rowCount = sheet.getPhysicalNumberOfRows();
            for (int rowNum = 1; rowNum < rowCount; rowNum++) {
                Row rowData = sheet.getRow(rowNum);
                if (rowData != null) {
                    // 读取列
                    int cellCount = rowTitle.getPhysicalNumberOfCells();
                    for (int cellNum = 0; cellNum < cellCount; cellNum++) {
                        System.out.print("[" + (rowNum + 1) + "-" + (cellNum + 1)+"]");
    
                        Cell cell = rowData.getCell(cellNum);
                        // 匹配数据的类型
                        if (cell!=null){
                            int cellType = cell.getCellType();
                            String cellValue="";
                            switch (cellType){
                                case Cell.CELL_TYPE_STRING: // 字符串
                                    System.out.print("[String]");
                                    cellValue=cell.getStringCellValue();
                                    break;
                                case Cell.CELL_TYPE_NUMERIC:    //数字
                                    System.out.print("[numeric]");
                                    if (HSSFDateUtil.isCellDateFormatted(cell)){    //日期
                                        System.out.print("[日期]");
                                        Date date = cell.getDateCellValue();
                                        cellValue = new DateTime(date).toString();
                                    }else {
                                        // 不是日期格式,防止数字过长
                                        System.out.print("[转换为字符串输出]");
                                        cell.setCellType(Cell.CELL_TYPE_STRING);
                                        cellValue = cell.toString();
                                    }
                                    break;
                                default:
                                    System.out.print("[数据类型错误!]");
                                    break;
                            }
                            System.out.println(cellValue);
                        }
                    }
                }
            }
    
            // 关闭流
            inputStream.close();
        }
    

    注意类型转换!!

    计算公式(了解即可)

     @Test
        public void testWrite07BigDataSecond() throws Exception {
    
            // 时间
            long begin = System.currentTimeMillis();
            // 创建一个簿
            Workbook workbook = new SXSSFWorkbook();
            //创建表
            Sheet sheet = workbook.createSheet();
            //写入数据
            for (int rowNum = 0; rowNum < 200000; rowNum++) {
                Row row = sheet.createRow(rowNum);
                for (int cellNum = 0; cellNum < 10; cellNum++) {
                    Cell cell = row.createCell(cellNum);
                    cell.setCellValue(cellNum);
                }
            }
            System.out.println("over");
    
            // 输出文件
            FileOutputStream outputStream = new FileOutputStream(PATH + "testWrite07BigDataSecond.xlsx");
            workbook.write(outputStream);
            // 关闭流
            outputStream.close();
    
            // 清除临时文件!
            ((SXSSFWorkbook)workbook).dispose();
    
            long end = System.currentTimeMillis();
            System.out.println((double)(end-begin)/1000);
        }
    

    4.EasyExcel相关的操作

    EasyExcel官方示例

    导入依赖

    <dependency>
    	<groupId>com.alibaba</groupId>
    	<artifactId>easyexcel</artifactId>
    	<version>1.1.2-beat1</version>
    </dependency>
    

    写入测试

    在这里插入图片描述
    对象

    @Data
    public class DemoData {
        @ExcelProperty("字符串标题")
        private String string;
        @ExcelProperty("日期标题")
        private Date date;
        @ExcelProperty("数字标题")
        private Double doubleData;
        /**
         * 忽略这个字段
         */
        @ExcelIgnore
        private String ignore;
    }
    

    1.DemoData.java

    /**
     * 最简单的写
     * <p>1. 创建excel对应的实体对象 参照{@link DemoData}
     * <p>2. 直接写即可
     */
    @Test
    public void simpleWrite() {
        // 写法1
        String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
        // 上面那个fileName不一定非得按人家的格式写,只要能保证该文件能确定一个文件就可以了
        // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
        // 如果这里想使用03 则 传入excelType参数即可
        /*他这个默认是xlsx格式的,第一个参数是文件名(绝对路径),
        	第二个参数是哪个模板,比如上面定义的DemoData对象
        	sheet("模板") 是说生成的工作簿(fileName)中含有一个叫“模板”的表
        	doWrite中的参数 data()返回的就是需要写入的数据!
    	*/
        EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
    }
    

    PS:我这里的data()是这样写的

    private List<DemoData> data() {
            List<DemoData> list = new ArrayList<DemoData>();
            for (int i = 0; i < 10; i++) {
                DemoData data = new DemoData();
                data.setString("字符串" + i);
                data.setDate(new Date());
                data.setDoubleData(0.56);
                list.add(data);
            }
    
            return list;
        }
    

    2.测试写入数据

    @Test测试下吧

    最终的结果

    如案例图所示

    展开全文
  • Netty是一款提供异步的、事件驱动的网络应用程序框架和工具,是基于NIO客户端、服务器端的编程框架。所以这里我们先以NIO和依赖相关的基础铺垫来进行剖析讲解,从而作为Netty学习之旅的一个开端。 Kafka的多副本冗余...

    前言

    这段时间一直在学习Netty相关知识,因为涉及知识点比较多,也走了不少弯路。目前网上关于Netty学习资料琳琅满目,不知如何下手,其实大家都是一样的,学习方法和技巧都是总结出来的,我们在没有找到很好的方法之前不如按部就班先从基础开始,一般从总分总的渐进方式,既观森林,又见草木。

    Netty是一款提供异步的、事件驱动的网络应用程序框架和工具,是基于NIO客户端、服务器端的编程框架。所以这里我们先以NIO和依赖相关的基础铺垫来进行剖析讲解,从而作为Netty学习之旅的一个开端。

    Kafka的多副本冗余设计

    不管是传统的基于关系型数据库设计的系统,还是分布式的如zookeeperredisKafkaHDFS等等,实现高可用的办法通常是采用冗余设计,通过冗余来解决节点宕机不可用问题。

    首先简单了解Kafka的几个概念:

    • 物理模型

    • 逻辑模型

    • Broker(节点):Kafka服务节点,简单来说一个Broker就是一台Kafka服务器,一个物理节点。

    • Topic(主题):在Kafka中消息以主题为单位进行归类,每个主题都有一个Topic Name,生产者根据Topic Name将消息发送到特定的Topic,消费者则同样根据Topic Name从对应的Topic进行消费。

    • Partition(分区):Topic(主题)是消息归类的一个单位,但每一个主题还能再细分为一个或多个Partition(分区),一个分区只能属于一个主题。主题和分区都是逻辑上的概念,举个例子,消息1和消息2都发送到主题1,它们可能进入同一个分区也可能进入不同的分区(所以同一个主题下的不同分区包含的消息是不同的),之后便会发送到分区对应的Broker节点上。

    • Offset(偏移量):分区可以看作是一个只进不出的队列(Kafka只保证一个分区内的消息是有序的),消息会往这个队列的尾部追加,每个消息进入分区后都会有一个偏移量,标识该消息在该分区中的位置,消费者要消费该消息就是通过偏移量来识别。

    其实,根据上述的几个概念,是不是也多少猜到了Kafka的多副本冗余设计实现了?别急,咱继续往下看。

    在Kafka 0.8版本以前,是没有多副本冗余机制的,一旦一个节点挂掉,那么这个节点上的所有Partition的数据就无法再被消费。这就等于发送到Topic的有一部分数据丢失了。

    在0.8版本后引入副本记者则很好地解决宕机后数据丢失的问题。副本是以Topic中每个Partition的数据为单位,每个Partition的数据会同步到其他物理节点上,形成多个副本。

    每个Partition的副本都包括一个Leader副本和多个Follower副本,Leader由所有的副本共同选举得出,其他副本则都为Follower副本。在生产者写或者消费者读的时候,都只会与Leader打交道,在写入数据后Follower就会来拉取数据进行数据同步。

    就这么简单?是的,基于上面这张多副本架构图就实现了Kafka的高可用。当某个Broker挂掉了,甭担心,这个Broker上的Partition在其他Broker节点上还有副本。你说如果挂掉的是Leader怎么办?那就在Follower中在选举出一个Leader即可,生产者和消费者又可以和新的Leader愉快地玩耍了,这就是高可用。

    你可能还有疑问,那要多少个副本才算够用?Follower和Leader之间没有完全同步怎么办?一个节点宕机后Leader的选举规则是什么?

    直接抛结论:

    多少个副本才算够用? 副本肯定越多越能保证Kafka的高可用,但越多的副本意味着网络、磁盘资源的消耗更多,性能会有所下降,通常来说副本数为3即可保证高可用,极端情况下将replication-factor参数调大即可。

    Follower和Lead之间没有完全同步怎么办? Follower和Leader之间并不是完全同步,但也不是完全异步,而是采用一种ISR机制(In-Sync Replica)。每个Leader会动态维护一个ISR列表,该列表里存储的是和Leader基本同步的Follower。如果有Follower由于网络、GC等原因而没有向Leader发起拉取数据请求,此时Follower相对于Leader是不同步的,则会被踢出ISR列表。所以说,ISR列表中的Follower都是跟得上Leader的副本。

    一个节点宕机后Leader的选举规则是什么? 分布式相关的选举规则有很多,像Zookeeper的ZabRaftViewstamped Replication、微软的PacificA等。而Kafka的Leader选举思路很简单,基于我们上述提到的ISR列表,当宕机后会从所有副本中顺序查找,如果查找到的副本在ISR列表中,则当选为Leader。另外还要保证前任Leader已经是退位状态了,否则会出现脑裂情况(有两个Leader)。怎么保证?Kafka通过设置了一个controller来保证只有一个Leader。

    Ack参数决定了可靠程度

    另外,这里补充一个面试考Kafka高可用必备知识点:request.required.asks参数。

    Asks这个参数是生产者客户端的重要配置,发送消息的时候就可设置这个参数。该参数有三个值可配置:0、1、All

    第一种是设为0,意思是生产者把消息发送出去之后,之后这消息是死是活咱就不管了,有那么点发后即忘的意思,说出去的话就不负责了。不负责自然这消息就有可能丢失,那就把可用性也丢失了。

    第二种是设为1,意思是生产者把消息发送出去之后,这消息只要顺利传达给了Leader,其他Follower有没有同步就无所谓了。存在一种情况,Leader刚收到了消息,Follower还没来得及同步Broker就宕机了,但生产者已经认为消息发送成功了,那么此时消息就丢失了。注意,设为1是Kafka的默认配置!!!可见Kafka的默认配置也不是那么高可用,而是对高可用和高吞吐量做了权衡折中。

    第三种是设为All(或者-1),意思是生产者把消息发送出去之后,不仅Leader要接收到,ISR列表中的Follower也要同步到,生产者才会任务消息发送成功。

    进一步思考,Asks=All就不会出现丢失消息的情况吗?答案是否。当ISR列表只剩Leader的情况下,Asks=All相当于Asks=1,这种情况下如果节点宕机了,还能保证数据不丢失吗?因此只有在Asks=All并且有ISR中有两个副本的情况下才能保证数据不丢失。

    解决问题

    绕了一大圈,了解了Kafka的高可用机制,终于回到我们一开始的问题本身,Kafka的一个节点宕机后为什么不可用?

    我在开发测试环境配置的Broker节点数是3,Topic是副本数为3,Partition数为6,Asks参数为1。

    当三个节点中某个节点宕机后,集群首先会怎么做?没错,正如我们上面所说的,集群发现有Partition的Leader失效了,这个时候就要从ISR列表中重新选举Leader。如果ISR列表为空是不是就不可用了?并不会,而是从Partition存活的副本中选择一个作为Leader,不过这就有潜在的数据丢失的隐患了。

    所以,只要将Topic副本个数设置为和Broker个数一样,Kafka的多副本冗余设计是可以保证高可用的,不会出现一宕机就不可用的情况(不过需要注意的是Kafka有一个保护策略,当一半以上的节点不可用时Kafka就会停止)。那仔细一想,Kafka上是不是有副本个数为1的Topic?

    问题出在了__consumer_offset上,__consumer_offset是一个Kafka自动创建的Topic,用来存储消费者消费的offset(偏移量)信息,默认Partition数为50。而就是这个Topic,它的默认副本数为1。如果所有的Partition都存在于同一台机器上,那就是很明显的单点故障了!当将存储__consumer_offset的Partition的Broker给Kill后,会发现所有的消费者都停止消费了。

    这个问题怎么解决?

    第一点,需要将__consumer_offset删除,注意这个Topic时Kafka内置的Topic,无法用命令删除,我是通过将logs删了来实现删除。

    第二点,需要通过设置offsets.topic.replication.factor为3来将__consumer_offset的副本数改为3。

    通过将__consumer_offset也做副本冗余后来解决某个节点宕机后消费者的消费问题。

    最后,关于为什么__consumer_offset的Partition会出现只存储在一个Broker上而不是分布在各个Broker上感到困惑,如果有朋友了解的烦请指教~

    总结

    总体来说,如果你想转行从事程序员的工作,Java开发一定可以作为你的第一选择。但是不管你选择什么编程语言,提升自己的硬件实力才是拿高薪的唯一手段。

    如果你以这份学习路线来学习,你会有一个比较系统化的知识网络,也不至于把知识学习得很零散。我个人是完全不建议刚开始就看《Java编程思想》、《Java核心技术》这些书籍,看完你肯定会放弃学习。建议可以看一些视频来学习,当自己能上手再买这些书看又是非常有收获的事了。

    这些视频如果需要的话,可以无偿分享给大家,点击这里即可免费领取

    又是非常有收获的事了。

    这些视频如果需要的话,可以无偿分享给大家,点击这里即可免费领取

    展开全文
  • POI的介绍 ...而我们Java中,也是提供了相应的操作办公软件的框架,其中,最常用的是下面两种: jxl:只能对Excel进行操作,属于比较老的框架。 POI:是apache的项目,可对ms的word,Excel,PPT进...
  • 我们使用到的是alibaba的开源框架《EasyExcel》 1、导入相应的依赖包 Maven <!-- https://mvnrepository.com/artifact/org.apache.poi/poi --> <dependency> <groupId>org.apache.poi</...
  • java解析复杂excel表格并导入数据库

    千次阅读 2021-01-08 11:52:26
    最近接到一个需求,需要把一份37万的excel数据解析并导入数据库, 分析 表格格式相对复杂包含很多合并单元格, 不符合通过navicat直接导入的要求, 数据量比较大, 建议一次保存25条,否则会导致内存泄漏; 代码 1.引入依赖...
  • java实现excel表格导入数据库表

    万次阅读 多人点赞 2018-11-07 15:07:14
    导入excel就是一个上传excel文件,然后获取excel文件数据,然后处理数据并插入到数据库的过程 一、上传excel 前端jsp页面,我的是index.jsp 在页面中我自己加入了一个下载上传文件的功能,其中超链接就是下载 ...
  • 使用Java操作excel的几种方法

    千次阅读 多人点赞 2021-03-29 09:49:55
    在平时的业务系统开发中,少不了需要用到导出、导入excel功能,今天我们就一起来总结一下! 下面给大家介绍一下几种常用方法: 1、apache poi 2、easypoi 3、easyexcel
  • Java圆形电子时钟源代码 1个目标文件 内容索引:JAVA源码,系统相关,电子用JAVA编写的指针式圆形电子钟,效果图如下所示,其实代码很简单,希望对你有帮助。 Message-Driven Bean EJB实例源代码 2个目标文件 摘要:...
  • java 导出excel表格1.前言2.项目环境3.maven依赖4. 代码演示3.1.创建表头3.2 绘制数据5.整体代码6. 导出到本地配置6.1nginx配置6.2前端代码 1.前言 前几周,划水划的正嗨,客户突然说要加个数据批量导出成excel表格...
  • 声明:本文是基于其他博主的文章,自己再稍微改了一点【公共类+业务类】,感谢那个博主 参考地址:... 控制层: /** * 导出数据到Excel * @param response 响应体 * */ @RequestMapping("/o...
  • java web导出excel表格(SSM框架

    千次阅读 2018-01-16 10:30:08
    // 第三步,在sheet中添加表头第0行,注意老版本poi对Excel的行数列数有限制short HSSFRow row = sheet.createRow((int) 0); // 第四步,创建单元格,并设置值表头 设置表头居中 HSSFCellStyle style = wb....
  • ![图片说明](https://img-ask.csdn.net/upload/201811/15/1542250128_321901.png)
  • List,String>> dataList = ExcelRead.readExcel(file); CmpRoleDetail bean = new CmpRoleDetail(); for(int i = 1;i();i++){ bean.setCreateOaCode(CommonUtil.oaCode()); bean.setCreateOaName...
  • java读写excel、csv文件

    2015-08-07 10:32:15
    文件中包括读取excel、csv文件,同时可以将读取出的文件内容保存在另外一个excel或csv中。注意excel2003和excel2007调用的jar包也不一样,需要将代码进行修改。2007版的将HSSF改成XSSF,2003相反。csv文件XSSF
  • 教师模块:教师可以通过导入Excel表格的方式进行添加试卷,如果Excel表中有不合法的数据,会在前台提醒哪一行哪一列出了什么问题,添加试卷后,教师可以发布试卷,试卷发布后,学生就可以答题,每张试卷都有作答时长...
  • 一、背景 1.Undertow 是红帽公司开发的一款基于 NIO 的高性能 Web 嵌入式服务器。...3.在SpringBoot框架中,我们使用最多的是Tomcat,这是SpringBoot默认的容器技术,而且是内嵌式的Tomcat。同时...
  • java中对Excel进行读写操作,目前常见的技术有:POI、EasyExcel ​ Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能。 ​ 对Excel操作的入门文档,...
  • 我使用的是Springboot框架开发的。首先需要在pom.xml文件中引入以下maven包: <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> ...
  • Java操作Excel并显示到网页

    千次阅读 2020-05-22 21:34:54
    Java 实现操作 excel 使用 POI 环境搭建 创建一个 maven 工程,pom.xml 中导入以下依赖: <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-...
  • java解析excel表格的信息 这几天工作就是解析一大堆的Excel表格,期间遇到的问题就是一个空异常过时了(或者说我代码哪里写错了,就是跳不过去,报错),还有就是解析时间列打印出来的是一个数字 先说明第一个问题,...
  • 该程序是一个基于 Apache POI 和 Antlr4 打造的 excel 生成的 Java 工具,主要作用在于减少了开发人员通过程序生成表格的工作量,提高了工作效率。 Chimm.Excel 的优势是什么? 和网上部分的开源软件的区别是,该...
  • java中如何导出excel表格

    千次阅读 2019-02-02 17:41:01
    如果你想导入excel:请参考:...本框架使用ssm。 1:点击导出订单,按照你的查询田间导出数据,如果没有查询条件就全部导出。 2:在调用工具类的时候需要导包,...
  • SSH框架中关于EXCEL表格导入到MySQL数据库
  • Java 生成Excel表格

    千次阅读 2016-03-31 11:29:12
    jxl全程为:Java Excel,该API可以用来在Java中对微软的Excel表格进行操作。下面,我来简单地介绍下它的使用方法。 接着,编写excelUtil工具类,用于生成Excel表格:package com.web.util;import java.io.File; ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 17,991
精华内容 7,196
关键字:

java操作电子表格用什么框架

java 订阅