SpringBoot+Vue+POI实现Excel的导入与导出

文章目录

  • 前言
  • POI中文操作API文档
  • 导入Excel文件
  • 导出Excel
  • 总结


前言

继上一篇Excel的模板下载后,就此更新企业开发中常常需要使用到的Excel的导入与导出。Excel的解析需要用到的是Apache下的一个操作Excel的工具POI,下面也会有详细介绍。


POI中文操作API文档

一、 POI简介

Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能。

二、 HSSF概况

HSSF 是Horrible SpreadSheet Format的缩写,通过HSSF,你可以用纯Java代码来读取、写入、修改Excel文件。HSSF为读取操作提供了两类API:usermodel和eventusermodel,即“用户模型”和“事件-用户模型”。

三、 POI EXCEL文档结构类

        HSSFWorkbook excel文档对象

        HSSFSheet excel的sheet HSSFRow excel的行

        HSSFCell excel的单元格 HSSFFont excel字体

        HSSFName 名称 HSSFDataFormat 日期格式

        HSSFHeader sheet头

        HSSFFooter sheet尾

        HSSFCellStyle cell样式

        HSSFDateUtil 日期

        HSSFPrintSetup 打印

        HSSFErrorConstants 错误信息表

四、 EXCEL常用操作方法

1、 得到Excel常用对象

POIFSFileSystem fs=newPOIFSFileSystem(new FileInputStream("d:/test.xls"));   
//得到Excel工作簿对象    
HSSFWorkbook wb = new HSSFWorkbook(fs);  
//得到Excel工作表对象    
HSSFSheet sheet = wb.getSheetAt(0);   
//得到Excel工作表的行    
HSSFRow row = sheet.getRow(i);  
//得到Excel工作表指定行的单元格    
HSSFCell cell = row.getCell((short) j);  
//得到单元格样式
cellStyle = cell.getCellStyle();  

2、建立Excel常用对象

//创建Excel工作簿对象 
HSSFWorkbook wb = new HSSFWorkbook();  
//创建Excel工作表对象     
HSSFSheet sheet = wb.createSheet("new sheet");
//创建Excel工作表的行 
HSSFRow row = sheet.createRow((short)0);   
//创建单元格样式  
cellStyle = wb.createCellStyle(); 
//创建Excel工作表指定行的单元格   
row.createCell((short)0).setCellStyle(cellStyle); 
//设置Excel工作表的值  
row.createCell((short)0).setCellValue(1);   

3、设置sheet名称和单元格内容

wb.setSheetName(1, "第一张工作表",HSSFCell.ENCODING_UTF_16);          
cell.setEncoding((short) 1);      
cell.setCellValue("单元格内容");

4、取得sheet的数目

wb.getNumberOfSheets();   

5、 根据index取得sheet对象

HSSFSheet sheet = wb.getSheetAt(0); 

6、取得有效的行数

int rowcount = sheet.getLastRowNum();  

7、取得一行的有效单元格个数

row.getLastCellNum();   

8、单元格值类型读写

//设置单元格为STRING类型   
cell.setCellType(HSSFCell.CELL_TYPE_STRING); 
//读取为数值类型的单元格内容
cell.getNumericCellValue();  

9、设置列宽、行高

sheet.setColumnWidth((short)column,(short)width);      
row.setHeight((short)height);    

10、添加区域,合并单元格

//合并从第rowFrom行columnFrom列
Region region = new Region((short)rowFrom,(short)columnFrom,(short)rowTo  ,(short)columnTo);   
// 到rowTo行columnTo的区域 
sheet.addMergedRegion(region);     
//得到所有区域       
sheet.getNumMergedRegions()  

11、保存Excel文件

FileOutputStream fileOut = new FileOutputStream(path);   
wb.write(fileOut);   

12、根据单元格不同属性返回字符串数值

public String getCellStringValue(HSSFCell cell) {      
        String cellValue = "";      
        switch (cell.getCellType()) {      
        case HSSFCell.CELL_TYPE_STRING://字符串类型   
            cellValue = cell.getStringCellValue();      
            if(cellValue.trim().equals("")||cellValue.trim().length()<=0)      
                cellValue=" ";      
            break;      
        case HSSFCell.CELL_TYPE_NUMERIC: //数值类型   
            cellValue = String.valueOf(cell.getNumericCellValue());      
            break;      
        case HSSFCell.CELL_TYPE_FORMULA: //公式   
            cell.setCellType(HSSFCell.CELL_TYPE_NUMERIC);      
            cellValue = String.valueOf(cell.getNumericCellValue());      
            break;      
        case HSSFCell.CELL_TYPE_BLANK:      
            cellValue=" ";      
            break;      
        case HSSFCell.CELL_TYPE_BOOLEAN:      
            break;      
        case HSSFCell.CELL_TYPE_ERROR:      
            break;      
        default:      
            break;      
        }      
        return cellValue;      
    }     

13、常用单元格边框格式

HSSFCellStyle style = wb.createCellStyle();
//下边框      
style.setBorderBottom(HSSFCellStyle.BORDER_DOTTED);  
//左边框      
style.setBorderLeft(HSSFCellStyle.BORDER_DOTTED); 
//右边框         
style.setBorderRight(HSSFCellStyle.BORDER_THIN); 
//上边框      
style.setBorderTop(HSSFCellStyle.BORDER_THIN);

14、设置字体和内容位置

HSSFFont f  = wb.createFont();  
//字号      
f.setFontHeightInPoints((short) 11);   
//加粗    
f.setBoldweight(HSSFFont.BOLDWEIGHT_NORMAL);     
style.setFont(f);      
//左右居中   
style.setAlignment(HSSFCellStyle.ALIGN_CENTER);   
//上下居中        
style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
//单元格内容的旋转的角度
style.setRotation(short rotation);       
HSSFDataFormat df = wb.createDataFormat();  
//设置单元格数据格式    
style1.setDataFormat(df.getFormat("0.00%")); 
//给单元格设公式       
cell.setCellFormula(string);   
//单元格内容的旋转的角度   
style.setRotation(short rotation); 

15、插入图片

//先把读进来的图片放到一个ByteArrayOutputStream中,以便产生ByteArray       
ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();      
BufferedImage bufferImg = ImageIO.read(new File("ok.jpg"));      
ImageIO.write(bufferImg,"jpg",byteArrayOut);      
//读进一个excel模版       
FileInputStream fos = new FileInputStream(filePathName+"/stencil.xlt");       
fs = new POIFSFileSystem(fos);      
//创建一个工作薄       
HSSFWorkbook wb = new HSSFWorkbook(fs);      
HSSFSheet sheet = wb.getSheetAt(0);      
HSSFPatriarch patriarch = sheet.createDrawingPatriarch();      
HSSFClientAnchor anchor = new HSSFClientAnchor(0,0,1023,255,(short) 0,0,(short)10,10);           
patriarch.createPicture(anchor , wb.addPicture(byteArrayOut.toByteArray(),HSSFWorkbook.PICTURE_TYPE_JPEG));    

16、调整工作表位置

HSSFWorkbook wb = new HSSFWorkbook();     
HSSFSheet sheet = wb.createSheet("format sheet");     
HSSFPrintSetup ps = sheet.getPrintSetup();     
sheet.setAutobreaks(true);     
ps.setFitHeight((short)1);     
ps.setFitWidth((short)1);   

17、设置打印区域

HSSFSheet sheet = wb.createSheet("Sheet1");     
wb.setPrintArea(0, "$A$1:$C$2");    

18、标注脚注

HSSFSheet sheet = wb.createSheet("format sheet");     
HSSFFooter footer = sheet.getFooter()     
footer.setRight( "Page " + HSSFFooter.page() + " of " + HSSFFooter.numPages() );  

19、在工作单中清空行数据,调整行位置

HSSFWorkbook wb = new HSSFWorkbook();     
HSSFSheet sheet = wb.createSheet("row sheet");     
// Create various cells and rows for spreadsheet.      
// Shift rows 6 - 11 on the spreadsheet to the top (rows 0 - 5)      
sheet.shiftRows(5, 10, -5);    

20、选中指定的工作表

HSSFSheet sheet = wb.createSheet("row sheet");     
heet.setSelected(true); 

21、工作表的放大缩小

HSSFSheet sheet1 = wb.createSheet("new sheet");     
sheet1.setZoom(1,2);   // 50 percent magnification    

22、头注和脚注

HSSFSheet sheet = wb.createSheet("new sheet");     
HSSFHeader header = sheet.getHeader();     
header.setCenter("Center Header");     
header.setLeft("Left Header");     
header.setRight(HSSFHeader.font("Stencil-Normal", "Italic") +     
HSSFHeader.fontSize((short) 16) + "Right w/ Stencil-Normal Italic font and size 16");  

23、自定义颜色

HSSFCellStyle style = wb.createCellStyle();     
style.setFillForegroundColor(HSSFColor.LIME.index);     
style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);     
HSSFFont font = wb.createFont();     
font.setColor(HSSFColor.RED.index);     
style.setFont(font);     
cell.setCellStyle(style);  

24、填充和颜色设置

HSSFCellStyle style = wb.createCellStyle();     
style.setFillBackgroundColor(HSSFColor.AQUA.index);     
style.setFillPattern(HSSFCellStyle.BIG_SPOTS);     
HSSFCell cell = row.createCell((short) 1);     
cell.setCellValue("X");     
style = wb.createCellStyle();     
style.setFillForegroundColor(HSSFColor.ORANGE.index);     
style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);     
cell.setCellStyle(style);   

25、强行刷新单元格公式

HSSFFormulaEvaluator eval=new HSSFFormulaEvaluator((HSSFWorkbook) wb);    
private static void updateFormula(Workbook wb,Sheet s,int row){     
        Row r=s.getRow(row);     
        Cell c=null;     
        FormulaEcaluator eval=null;     
        if(wb instanceof HSSFWorkbook)     
            eval=new HSSFFormulaEvaluator((HSSFWorkbook) wb);     
        else if(wb instanceof XSSFWorkbook)     
            eval=new XSSFFormulaEvaluator((XSSFWorkbook) wb);     
        for(int i=r.getFirstCellNum();i
            c=r.getCell(i);     
            if(c.getCellType()==Cell.CELL_TYPE_FORMULA)     
                eval.evaluateFormulaCell(c);     
        }     
    }    

说明:FormulaEvaluator提供了evaluateFormulaCell(Cell cell)方法,计算公式保存结果,但不改变公式。而evaluateInCell(Cell cell) 方法是计算公式,并将原公式替换为计算结果,也就是说该单元格的类型不在是Cell.CELL_TYPE_FORMULA而是Cell.CELL_TYPE_NUMBERIC。HSSFFormulaEvaluator提供了静态方法evaluateAllFormulaCells(HSSFWorkbook wb) ,计算一个Excel文件的所有公式,用起来很方便。

-------------------------------------------poi 方法总结-------------------------------------------------------------------

1.设置不显示excel网格线

sheet.setDisplayGridlines(false);其中sheet是Sheet对象

2.设置excel单元格中的内容换行

cellStyle.setWrapText(true);其中cellStyle是WorkBook创建的CellStyle对象,然后将cellStyle设置到要换行的Cell对象,最后在要换行的对象(一般为字符串)加入"/r/n"。如
topTile.append("/r/n" +“cellContent”);

3.单元格的合并

sheet.addMergedRegion(new CellRangeAddress(0, 4, 0, 2));本示例为合并4行2列

4.设置页眉和页脚的页数

HSSFHeader header = sheet.getHeader();
header.setCenter(“Center Header”);
header.setLeft(“Left Header”);
header.setRight(HSSFHeader.font(“Stencil-Normal”, “Italic”) +
HSSFHeader.fontSize((short) 16) + “Right w/ Stencil-Normal Italic font and size 16”);

HSSFFooter footer = (HSSFFooter )sheet.getFooter()
footer.setRight( "Page " + HSSFFooter.page() + " of " +
HSSFFooter.numPages() );

5.使得一个Sheet适合一页

sheet.setAutobreaks(true);

6.设置放大属性(Zoom被明确为一个分数,例如下面的75%使用3作为分子,4作为分母)

sheet.setZoom(3,4);

7.设置打印

HSSFPrintSetup print = (HSSFPrintSetup) sheet.getPrintSetup();
print.setLandscape(true);//设置横向打印 print.setScale((short)
70);//设置打印缩放70%
print.setPaperSize(HSSFPrintSetup.A4_PAPERSIZE);//设置为A4纸张
print.setLeftToRight(true);//設置打印顺序先行后列,默认为先列行
print.setFitHeight((short) 10);设置缩放调整为10页高
print.setFitWidth((short) 10);设置缩放调整为宽高

sheet.setAutobreaks(false); if (i != 0 && i % 30 == 0)
sheet.setRowBreak(i);//設置每30行分頁打印

8.反复的行和列(设置打印标题) HSSFWorkbook wb = new HSSFWorkbook(); wb.setRepeatingRowsAndColumns(0, 0, 12, 1, 6);//设置1到12列,行1到6每一页重复打印

9.调整单元格宽度

sheet.setAutobreaks(true);
  sheet.setColumnWidth((short)i,colsWidth[i]); //设定单元格长度
sheet.autoSizeColumn((short) i);//自动根据长度调整单元格长度

好了,下面我们进入正题,导入Excel。

导入Excel文件

这是本文导入的Excel样例
SpringBoot+Vue+POI实现Excel的导入与导出_第1张图片

分析 : 此Excel分为两部分,一是主表部分、二是子表部分,上面主表部分为码不多入库单的基本信息,下面子表为主表下的所有物资信息,所以我们要设计两张数据库表,来存储这些信息。我们在使用POI操作的时候也要分别读取主、子表的信息,它们基本上属于一对多的关系。

  1. 主表 :SpringBoot+Vue+POI实现Excel的导入与导出_第2张图片
  2. 子表 :SpringBoot+Vue+POI实现Excel的导入与导出_第3张图片

一、前端代码
1.导入按钮,绑定导入框,绑定事件方法,这里我将导入框隐藏了。页面显示的是导入按钮。

		
        <form id="form8">
                <p>
                <input type="file" id="f8" name="file"
                          onchange="vm.uploadfile()"
                          style="display:none"/>
                p>
         form>
         
         
         <button type="button" onclick="$('#f8').click() "class="miaoshu class= btn btn-outline-success" >
            导入RK单
        button>

2.JS方法uploadFile() 因为导入的话,需要拿到用户选择导入的Excel文件,所以这里要写个上传文件的方法,让后端获取到,如果下面的代码看不懂也没关系,你只要CV就好了,把重要的地方改一改,比如url。这里的uploadfile是写在Vue的methods中。

uploadfile() {
                var animateimg = $('#f8').val();
                var imgarr = animateimg.split('\\'); //分割
                var myimg = imgarr[imgarr.length - 1]; //去掉 // 获取图片名
                var houzui = myimg.lastIndexOf('.'); //获取 . 出现的位置
                var ext = myimg.substring(houzui, myimg.length).toUpperCase();  //切割 . 获取文件后缀
				//判断是否为Excel文件
                if (ext != '.XLS' && ext != '.XLSX') {
                    alert("请上传excel文件!");
                    window.history.go(0)
                } else {
                    var data = new FormData($("#form8")[0]);
                    //访问后端控制器
                    $.ajax({
                        url: "/materialNotpay/importRKD",
                        type: 'POST',
                        data: data,
                        cache: false,
                        processData: false,
                        contentType: false,
                        success: function (xhr111) {
                            window.history.go(0);
                        },
                        error: function () {
                        }
                    });
                }
            }

二、后端代码

说明 : 这里的后端信息我并没有采用实体类来存储信息,因为可能会出现一些问题,这里就不做过多解释了,这里使用的是Map集合与List集合来存储的信息数据,具体请看下面的代码演示。

1.首先我们要先引入POI的依赖

        
        <dependency>
            <groupId>org.apache.poigroupId>
            <artifactId>poiartifactId>
            <version>3.17-beta1version>
        dependency>
        
        <dependency>
            <groupId>org.apache.poigroupId>
            <artifactId>poi-ooxmlartifactId>
            <version>3.17-beta1version>
        dependency>

2.Mybatis映射文件sql

说明: 因为我们如果想要把基本信息对应的多个物资列表显示到前端,就需要根据基本信息的id去判断获取对应外键的物资信息,所以这里获取了插入sql的id。
如下 :
useGeneratedKeys=“true” keyProperty=“id” keyColumn=“id”

    
    <insert id="importRKD" parameterType="map"  useGeneratedKeys="true" keyProperty="id" keyColumn="id">
        insert into rkd_info
        <foreach collection="map" index="key" item="val" separator="," open="(" close=")">
            ${key}
        foreach>
        values
        <foreach collection="map" index="key" item="val" separator="," open="(" close=")">
            #{val}
        foreach>
    insert>

	
    <insert id="importRKDWuZi" parameterType="list">
        insert into
        rkd_wuziinfo(goods_serialNumbers,goods_code,goods_name,goods_type,goods_specifications,goods_brand,goods_source,goods_total,goods_price,goods_money,goods_date,goods_contractNumber,goods_validity,goods_annotation,rkd_goodsId)
        values
        <foreach collection="list" item="val" separator=",">
            <foreach collection="val" separator="," item="val" index="index" open="(" close=")">
                #{val}
            foreach>
        foreach>
    insert>

3.DAO层代码

 /**
     * 功能描述: 导入码不多入库单基础信息部分
     * @author 码不多
     * @date 2021/9/3
     * @param map
     * @return void
     */
    void importRKD(@Param("map") Map<String, String> map);
    /**
     * 功能描述: 导入码不多入库单子表物资部分
     * @author 码不多
     * @date 2021/9/3
     * @param arrayList
     * @return void
     */
    void importRKDWuZi(@Param("list") List<List> arrayList);

4.Service层代码

	//Service
	
  /**
     * 功能描述:导入码不多入库单基本信息部分
     * @author 码不多
     * @date 2021/9/3
     * @param map
     * @return void
     */
    void importRKD(Map<String, String> map);

    /**
     * 功能描述:导入码不多入库单子表物资部分
     * @author 码不多
     * @date 2021/9/3
     * @param arrayList
     * @return void
     */
    void importRKDWuZi(List<List> arrayList);

实现类 :

    /**
     * 功能描述: 导入码不多入库单基本信息部分
     * @author 码不多
     * @date 2021/9/3
     * @param map
     * @return void
     */
    @Override
    public void importRKD(Map<String,String> map) {
        materialNoPayDao.importRKD(map);
    }

    /**
     * 功能描述:导入码不多入库单子表物资部分
     * @author 码不多
     * @date 2021/9/3
     * @param arrayList
     * @return void
     */
    @Override
    public void importRKDWuZi(List<List> arrayList) {
        materialNoPayDao.importRKDWuZi(arrayList);
    }

5.POI判断列中数据类型获取列值工具类

import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.ss.usermodel.Cell;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author 码不多
 * @version 1.0
 * @description: 效验单元格数据类型获取值工具类
 * @date 2021/9/3 
 */
public class ExcelUtilTwo {
    /**
     * 功能描述: 获取列值
     * @author 码不多
     * @date 2021/9/3
     * @param cell
     * @return java.lang.String
     */
    public static String getCellValue(Cell cell) {
        String cellValue = "";
        if (cell == null) {
            return cellValue;
        }
        // 判断数据的类型
        switch (cell.getCellType()) {
            case Cell.CELL_TYPE_NUMERIC: // 数字
                //short s = cell.getCellStyle().getDataFormat();
                if (HSSFDateUtil.isCellDateFormatted(cell)) {// 处理日期格式、时间格式
                    SimpleDateFormat sdf = null;
                    // 验证short值
                    if (cell.getCellStyle().getDataFormat() == 14) {
                        sdf = new SimpleDateFormat("yyyy/MM/dd");
                    } else if (cell.getCellStyle().getDataFormat() == 21) {
                        sdf = new SimpleDateFormat("HH:mm:ss");
                    } else if (cell.getCellStyle().getDataFormat() == 22) {
                        sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
                    } else {
                        throw new RuntimeException("日期格式错误!!!");
                    }
                    Date date = cell.getDateCellValue();
                    cellValue = sdf.format(date);
                } else if (cell.getCellStyle().getDataFormat() == 0) {//处理数值格式
                    cell.setCellType(Cell.CELL_TYPE_STRING);
                    cellValue = String.valueOf(cell.getRichStringCellValue().getString());
                }
                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 = null;
                break;
            case Cell.CELL_TYPE_ERROR: // 故障
                cellValue = "非法字符";
                break;
            default:
                cellValue = "未知类型";
                break;
        }
        return cellValue;
    }
}

6.Controller控制器代码

说明 : 这里涉及到合并单元格的列读取,读取合并单元格的第一个坐标位置就可以获取到数据了。请求方法中的参数是拿到前端用户导入的Excel文件。

    /**
     * 功能描述: 导入入库单的Excel
     * @author 码不多
     * @date 2021/9/2
     * @param file
     * @return java.lang.String
     */
    @PostMapping("importRKD")
    @ResponseBody
    public String importRKD(@RequestParam("file") MultipartFile file){
        //先读主表,插主表
        try {
            //获取用户上传的Excel,并创建此Excel的工作簿对象
            Workbook workbook = WorkbookFactory.create(file.getInputStream());
            //创建集合用于存储Excel读出来的数据
            Map<String,String> map = new HashMap<>();
            //使用工作薄对象,获取第一个Sheet页
            Sheet sheetAt = workbook.getSheetAt(0);
            //获取所有的合并单元格
            List<CellRangeAddress> mergedRegions = sheetAt.getMergedRegions();
            //主表共占用3行
            //获取有内容的第一行,坐标为 (2,2) 代码中要减1
            Row row1 = sheetAt.getRow(1);
            //获取列
            Cell row1Cell1 = row1.getCell(2);
            Cell row1Cell2 = row1.getCell(6);
            Cell row1Cell3 = row1.getCell(10);
            //获取有内容的第二行
            Row row2 = sheetAt.getRow(2);
            //获取列
            Cell row2Cell1 = row2.getCell(2);
            Cell row2Cell2 = row2.getCell(6);
            //获取列的内容(单号)
            String row1CellContent1 = ExcelUtilTwo.getCellValue(row1Cell1);
            //获取列的内容(金额)
            String row1CellContent2 = ExcelUtilTwo.getCellValue(row1Cell2);
            //获取列的内容(日期)
            String row1CellContent3 = ExcelUtilTwo.getCellValue(row1Cell3);
            //获取列的内容(制单人)
            String row2CellContent1 = ExcelUtilTwo.getCellValue(row2Cell1);
            //获取列的内容(联系电话)
            String row2CellContent2 = ExcelUtilTwo.getCellValue(row2Cell2);

            //判断用户信息是否填写完整
            if (row1CellContent1!=null&&row1CellContent2!=null&&row1CellContent3!=null&&row2CellContent1!=null&&row2CellContent2!=null){
                map.put("rkd_number",row1CellContent1);
                map.put("rkd_money",row1CellContent2);
                map.put("rkd_date",row1CellContent3);
                map.put("rkd_person",row2CellContent1);
                map.put("rkd_phone",row2CellContent2);
            }
            //定义变量提高作用域保存基本信息的自增id
            String rkdInfoId = null;

            //判断Map中是否有数据
            if (map.size()!=0){
                //添加到码不多入库单基本信息表中
                RKDService.importRKD(map);
                //获取其自增的id
                Object obj = map.get("id");
                String i = String.valueOf(obj);
                //给上面的变量赋值
                rkdInfoId = i;
            }

            //解析子表
            //创建集合存储数据
            List<List> arrayList = new ArrayList();
            //获取有效行数
            int lastRowNum = sheetAt.getLastRowNum();
            //注意:一般实际开发中获取有效列大多数都以标题行为有效列就此文的Excel来说有效列为坐标3应该在这里获取
            //创建标题所在行
            Row rowTitle = sheetAt.getRow(3);
            //获取标题行有效列数做为子表单的有效列数
            short globalCellNum = rowTitle.getLastCellNum();
            //遍历子表所有行
            for (int numRow = 4; numRow < lastRowNum+1; numRow++){
                //创建集合存储每一行数据
                LinkedList list = new LinkedList();
                //创建遍历的当前行
                Row row = sheetAt.getRow(numRow);
                //判断当前行是否为空,空行就跳过
                if(row == null){
                	continue;
                }
                //遍历子表当前行所有的列
                for (int numCell = 0; numCell < globalCellNum ; numCell++){
                    //获取所有列
                    Cell cell = row.getCell(numCell);
                    //获取列内容
                    String cellValue = ExcelUtilTwo.getCellValue(cell);
                    //如果内容为null跳出,执行下一次循环
                    if (cellValue == null||cellValue.equals("")){
                        continue;
                    }
                    //将列数据添加到集合
                    list.add(cellValue);

                    //判断是否遍历到当前行的最后一列,在最后一列给其添加外键id绑定基础信息表
                    if (numCell == globalCellNum-1){
                        //给其添加外键id
                        list.add(rkdInfoId);
                    }
                }
                //行数据添加到集合
                arrayList.add(list);
            }

            //当物资子表并且基本信息也有数据的时候执行插入
            if (map.size()!=0&&arrayList.size()!=0){
                //将数据插入到物资子表
                Service.importRKDWuZi(arrayList);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        //跳转到页面显示信息,此处也是跳转的Controller,那个Contorller就是跳到页面将导入的信息显示出来我就不贴了,没什么实际意义。
        return "/info/mabuduo";
    }

效果 :
在这里插入图片描述在这里插入图片描述

name为null是因为没有没使用到这一列。下面只需要根据业务需求将解析到数据库中的数据取出显示到页面上就可以了。


导出Excel

本文要导出的信息列表图
SpringBoot+Vue+POI实现Excel的导入与导出_第4张图片

导出Excel后的效果图
在这里插入图片描述
分析 : 此Excel分为两部分,一是主表部分、二是子表部分,这些信息都是从数据库中查出来的,我们需要先查主表,然后再查子表部分。一般如果开发中需要导出Excel也会有一个Excel的格式规范。这些格式规范是固定死的,所以下面的讲解代码中会频繁的使用到索引与Excel的坐标位置。

一、前端代码

  1. 导出按钮
<button class="miaoshu btn btn-outline-success" type="button" onclick="vm.exportExcel(vm.rkd_MaBuDuo.id)" >导出入库单button>

这里注意 : 我们需要传递主表的id到后端,这样后端才能定位到具体的导入项,从而去根据这个主表的id去写sql查DB。

  1. 导出JS事件exportExcel( materialId )
            exportExcel(materialId){
                //定义url,并携带主表的id参数
                let url="/mabuduo/exportRkdList?materialId="+materialId;
                //判断是否有id为_exportForm的form表单,如果没有则创建一个隐藏的form,把url放入,然后submit
                let exportForm = document.getElementById("_exportForm");
                if (!exportForm){
                    exportForm = document.createElement("form");
                    exportForm.setAttribute('id',"_exportForm");
                    exportForm.setAttribute("action", url);
                    exportForm.setAttribute("method", "post");
                }
                document.body.appendChild(exportForm);
                exportForm.submit();
            }

这里注意 : 这个事件中的参数就是上面的按钮传递的当前主表的id。

二、后端代码

这里我们从前往后讲

  1. Controller层代码
    /**
     * 功能描述: 入库单导出
     * @author 码不多
     * @date 2021/9/12
     * @param id
     * @param response
     * @return
     */
    @RequestMapping("/exportRkdList")
    public void exportRkdList(@RequestParam("materialId") int id ,HttpServletResponse response){
        //查询入库单主表信息,返回映射到实体类
        InitRkdBasicInfoModel rkdBasicInfo = materialNoPayService.findRkdBasicInfo(id);
        //查询入库单子表物资信息(这里的""为此方法的必要参数,因为这个方法并不是我特意为了查子表的物资列表信息写的)
        List<Map> listWuZi = materialNoPayDao.finRKDWuZiById(String.valueOf(id), "");

        //创建工作薄对象
        Workbook workbook = new HSSFWorkbook();
        //创建Sheet页,给其命名
        Sheet sheet = workbook.createSheet("码不多入库单");

        //设置单元格样式
        CellStyle cellStyle = workbook.createCellStyle();
        //设置内容水平居中
        cellStyle.setAlignment(HorizontalAlignment.CENTER);

        //创建行对象,第一行
        Row row = sheet.createRow(0);
        //创建列对象,第一列
        Cell cell = row.createCell(0);
        //设置列值(标题)
        cell.setCellValue("码不多入库单");
        //设置列样式
        cell.setCellStyle(cellStyle);
        //合并单元格CellRangeAddress构造参数依次表示起始行,截至行,起始列, 截至列
        sheet.addMergedRegion(new CellRangeAddress(0,0,0,14));
        //创建第二行
        Row row1 = sheet.createRow(1);
        //创建列
        Cell row1Cell0 = row1.createCell(0);
        //设置列值
        row1Cell0.setCellValue("入库单号");
        //设置列样式
        row1Cell0.setCellStyle(cellStyle);
        //合并单元格CellRangeAddress构造参数依次表示起始行,截至行,起始列, 截至列
        sheet.addMergedRegion(new CellRangeAddress(1,1,0,1));
        //创建列
        Cell row1Cell1 = row1.createCell(2);
        //设置列值
        row1Cell1.setCellValue(rkdBasicInfo.getRkd_number());
        //设置列样式
        row1Cell1.setCellStyle(cellStyle);
        //合并单元格CellRangeAddress构造参数依次表示起始行,截至行,起始列, 截至列
        sheet.addMergedRegion(new CellRangeAddress(1,1,2,4));
        //创建列
        Cell row1Cell2 = row1.createCell(5);
        //设置列值
        row1Cell2.setCellValue("入库金额");
        //设置列样式
        row1Cell2.setCellStyle(cellStyle);
        //创建列
        Cell row1Cell3 = row1.createCell(6);
        //设置列值
        row1Cell3.setCellValue(rkdBasicInfo.getRkd_money());
        //设置列样式
        row1Cell3.setCellStyle(cellStyle);
        //合并单元格CellRangeAddress构造参数依次表示起始行,截至行,起始列, 截至列
        sheet.addMergedRegion(new CellRangeAddress(1,1,6,8));
        //创建列
        Cell row1Cell4 = row1.createCell(9);
        //设置列值
        row1Cell4.setCellValue("入库日期");
        //设置列样式
        row1Cell4.setCellStyle(cellStyle);
        //创建列
        Cell row1Cell5 = row1.createCell(10);
        row1Cell5.setCellValue(rkdBasicInfo.getRkd_date());
        //设置列样式
        row1Cell5.setCellStyle(cellStyle);
        //合并单元格CellRangeAddress构造参数依次表示起始行,截至行,起始列, 截至列
        sheet.addMergedRegion(new CellRangeAddress(1,1,10,12));
        //创建列
        row1.createCell(13);
        //空、不需要赋值
        row1.createCell(14);
        //创建第三行
        Row row2 = sheet.createRow(2);
        //创建列
        Cell row2Cell1 = row2.createCell(0);
        //赋值
        row2Cell1.setCellValue("制单人");
        //设置列样式
        row2Cell1.setCellStyle(cellStyle);
        //合并单元格CellRangeAddress构造参数依次表示起始行,截至行,起始列, 截至列
        sheet.addMergedRegion(new CellRangeAddress(2,2,0,1));
        //创建列
        Cell row2Cell2 = row2.createCell(2);
        //设置列值
        row2Cell2.setCellValue(rkdBasicInfo.getRkd_person());
        //设置列样式
        row2Cell2.setCellStyle(cellStyle);
        //合并单元格CellRangeAddress构造参数依次表示起始行,截至行,起始列, 截至列
        sheet.addMergedRegion(new CellRangeAddress(2,2,2,4));
        //创建列
        Cell row2Cell3 = row2.createCell(5);
        //设置列值
        row2Cell3.setCellValue("联系电话");
        //设置列样式
        row2Cell3.setCellStyle(cellStyle);
        //创建列
        Cell row2Cell4 = row2.createCell(6);
        //设置列值
        row2Cell4.setCellValue(rkdBasicInfo.getRkd_phone());
        //设置列样式
        row2Cell4.setCellStyle(cellStyle);
        //合并单元格CellRangeAddress构造参数依次表示起始行,截至行,起始列, 截至列
        sheet.addMergedRegion(new CellRangeAddress(2,2,6,8));
        //创建列,为空不需要赋值
        row2.createCell(9);
        row2.createCell(10);
        row2.createCell(11);
        row2.createCell(12);
        row2.createCell(13);
        row2.createCell(14);
        //创建第四行
        Row row3 = sheet.createRow(3);
        //创建列,并赋值
        Cell row3Cell = row3.createCell(0);
        row3Cell.setCellValue("序号");
        //设置列样式
        row3Cell.setCellStyle(cellStyle);
        Cell row3Cell1 = row3.createCell(1);
        row3Cell1.setCellValue("物资编码");
        row3Cell1.setCellStyle(cellStyle);
        Cell row3Cell2 = row3.createCell(2);
        row3Cell2.setCellValue("物资名称");
        row3Cell2.setCellStyle(cellStyle);
        Cell row3Cell3 = row3.createCell(3);
        row3Cell3.setCellValue("物资分类");
        row3Cell3.setCellStyle(cellStyle);
        Cell row3Cell4 = row3.createCell(4);
        row3Cell4.setCellValue("物资规格");
        row3Cell4.setCellStyle(cellStyle);
        Cell row3Cell5 = row3.createCell(5);
        row3Cell5.setCellValue("品牌");
        row3Cell5.setCellStyle(cellStyle);
        Cell row3Cell6 = row3.createCell(6);
        row3Cell6.setCellValue("物资来源");
        row3Cell6.setCellStyle(cellStyle);
        Cell row3Cell7 = row3.createCell(7);
        row3Cell7.setCellValue("数量");
        row3Cell7.setCellStyle(cellStyle);
        Cell row3Cell8 = row3.createCell(8);
        row3Cell8.setCellValue("单位");
        row3Cell8.setCellStyle(cellStyle);
        Cell row3Cell9 = row3.createCell(9);
        row3Cell9.setCellValue("单价");
        row3Cell9.setCellStyle(cellStyle);
        Cell row3Cell10 = row3.createCell(10);
        row3Cell10.setCellValue("金额");
        row3Cell10.setCellStyle(cellStyle);
        Cell row3Cell11 = row3.createCell(11);
        row3Cell11.setCellValue("采购时间");
        row3Cell11.setCellStyle(cellStyle);
        Cell row3Cell12 = row3.createCell(12);
        row3Cell12.setCellValue("合同号");
        row3Cell12.setCellStyle(cellStyle);
        Cell row3Cell13 = row3.createCell(13);
        row3Cell13.setCellValue("有效期");
        row3Cell13.setCellStyle(cellStyle);
        Cell row3Cell14 = row3.createCell(14);
        row3Cell14.setCellValue("备注");
        row3Cell14.setCellStyle(cellStyle);

        //物资信息(子表),因为从第四行开始,所以要定义到4,集合长度也要相应+4
        for (int i = 4; i < listWuZi.size()+4; i++){
            //循环创建数据行
            Row rowWuZi = sheet.createRow(i);
            //循环创建列
            for (int k = 0; k < listWuZi.get(i-4).size(); k++){
                Cell cellWuZi = rowWuZi.createCell(k);
                //设置列样式
                cellWuZi.setCellStyle(cellStyle);
                //判断当前列为哪个数据
                if (k == 0){
                    //定义序号
                    int number = i-3;
                    //判断是否有数据
                    if (listWuZi.get(i - 4).get("goods_serialNumbers")==null){
                        //设置列数据值
                        /*cellWuZi.setCellValue("");*/
                        cellWuZi.setCellValue(number);
                    }else{
                        String goods_serialNumbers = String.valueOf(listWuZi.get(i - 4).get("goods_serialNumbers"));
                        //设置列数据值
                        cellWuZi.setCellValue(goods_serialNumbers);
                    }
                }else if (k == 1){
                    //判断是否有数据
                    if (listWuZi.get(i - 4).get("goods_code")==null){
                        //设置列数据值
                        cellWuZi.setCellValue("");
                    }else{
                        String goods_code = String.valueOf(listWuZi.get(i - 4).get("goods_code"));
                        //设置列数据值
                        cellWuZi.setCellValue(goods_code);
                    }
                }else if (k == 2){
                    //判断是否有数据
                    if (listWuZi.get(i - 4).get("goods_name")==null){
                        //设置列数据值
                        cellWuZi.setCellValue("");
                    }else{
                        String goods_name = String.valueOf(listWuZi.get(i - 4).get("goods_name"));
                        //设置列数据值
                        cellWuZi.setCellValue(goods_name);
                    }
                }else if (k == 3){
                    //判断是否有数据
                    if (listWuZi.get(i - 4).get("goods_type")==null){
                        //设置列数据值
                        cellWuZi.setCellValue("");
                    }else{
                        String goods_type = String.valueOf(listWuZi.get(i - 4).get("goods_type"));
                        //设置列数据值
                        cellWuZi.setCellValue(goods_type);
                    }
                }else if (k == 4){
                    //判断是否有数据
                    if (listWuZi.get(i - 4).get("goods_specifications")==null){
                        //设置列数据值
                        cellWuZi.setCellValue("");
                    }else{
                        String goods_specifications = String.valueOf(listWuZi.get(i - 4).get("goods_specifications"));
                        //设置列数据值
                        cellWuZi.setCellValue(goods_specifications);
                    }
                }else if (k == 5){
                    //判断是否有数据
                    if (listWuZi.get(i - 4).get("goods_brand")==null){
                        //设置列数据值
                        cellWuZi.setCellValue("");
                    }else{
                        String goods_brand = String.valueOf(listWuZi.get(i - 4).get("goods_brand"));
                        //设置列数据值
                        cellWuZi.setCellValue(goods_brand);
                    }
                }else if (k == 6){
                    //判断是否有数据
                    if (listWuZi.get(i - 4).get("goods_source")==null){
                        //设置列数据值
                        cellWuZi.setCellValue("");
                    }else{
                        String goods_source = String.valueOf(listWuZi.get(i - 4).get("goods_source"));
                        //设置列数据值
                        cellWuZi.setCellValue(goods_source);
                    }
                }else if(k==7){
                    //判断是否有数据
                    if (listWuZi.get(i - 4).get("goods_total")==null){
                        //设置列数据值
                        cellWuZi.setCellValue("");
                    }else{
                        String goods_total = String.valueOf(listWuZi.get(i - 4).get("goods_total"));
                        //设置列数据值
                        cellWuZi.setCellValue(goods_total);
                    }
                }else if(k==8){
                    //判断是否有数据
                    if (listWuZi.get(i - 4).get("good_measurement")==null){
                        //设置列数据值
                        cellWuZi.setCellValue("");
                    }else{
                        String good_measurement = String.valueOf(listWuZi.get(i - 4).get("good_measurement"));
                        //设置列数据值
                        cellWuZi.setCellValue(good_measurement);
                    }
                }else if(k==9){
                    //判断是否有数据
                    if (listWuZi.get(i - 4).get("goods_price")==null){
                        //设置列数据值
                        cellWuZi.setCellValue("");
                    }else{
                        String goods_price = String.valueOf(listWuZi.get(i - 4).get("goods_price"));
                        //设置列数据值
                        cellWuZi.setCellValue(goods_price);
                    }
                }else if(k==10){
                    //判断是否有数据
                    if (listWuZi.get(i - 4).get("goods_money")==null){
                        //设置列数据值
                        cellWuZi.setCellValue("");
                    }else{
                        String goods_money = String.valueOf(listWuZi.get(i - 4).get("goods_money"));
                        //设置列数据值
                        cellWuZi.setCellValue(goods_money);
                    }
                }else if(k==11){
                    //判断是否有数据
                    if (listWuZi.get(i - 4).get("goods_date")==null){
                        //设置列数据值
                        cellWuZi.setCellValue("");
                    }else{
                        String goods_date = String.valueOf(listWuZi.get(i - 4).get("goods_date"));
                        //设置列数据值
                        cellWuZi.setCellValue(goods_date);
                    }
                }else if(k==12){
                    //判断是否有数据
                    if (listWuZi.get(i - 4).get("goods_contractNumber")==null){
                        //设置列数据值
                        cellWuZi.setCellValue("");
                    }else{
                        String goods_contractNumber = String.valueOf(listWuZi.get(i - 4).get("goods_contractNumber"));
                        //设置列数据值
                        cellWuZi.setCellValue(goods_contractNumber);
                    }
                }else if(k==13){
                    //判断是否有数据
                    if (listWuZi.get(i - 4).get("goods_validity")==null){
                        //设置列数据值
                        cellWuZi.setCellValue("");
                    }else{
                        String goods_validity = String.valueOf(listWuZi.get(i - 4).get("goods_validity"));
                        //设置列数据值
                        cellWuZi.setCellValue(goods_validity);
                    }
                }else if(k==14){
                    //判断是否有数据
                    if (listWuZi.get(i - 4).get("goods_annotation")==null){
                        //设置列数据值
                        cellWuZi.setCellValue("");
                    }else{
                        String goods_annotation = String.valueOf(listWuZi.get(i - 4).get("goods_annotation"));
                        //设置列数据值
                        cellWuZi.setCellValue(goods_annotation);
                    }
                }
            }
        }


        try {
            String fileName = "码不多入库单.xls";//创建文件名(改)
            String fileNameURL = URLEncoder.encode(fileName, "UTF-8");
            //设置响应头
            response.setHeader("Content-disposition", "attachment;filename="+fileNameURL+";"+"filename*=utf-8''"+fileNameURL);
            //设置输出的内容类型
            response.setContentType("application/octet-stream");
            //将其写出
            workbook.write(response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
  1. Service层
    /**
     * 功能描述: 根据id查询入库单的基本信息(主表)
     * @author 码不多
     * @date 2021/9/7
     * @param id
     * @return com.buduo.entity.InitRkdBasicInfoModel
     */
    InitRkdBasicInfoModel findRkdBasicInfo(int id);

实现类

    /**
     * 功能描述: 根据id查询入库单的基本信息(主表)
     * @author 码不多
     * @date 2021/9/7
     * @param id
     * @return com.buduo.entity.InitRkdBasicInfoModel 
     */
    @Override
    public InitRkdBasicInfoModel findRkdBasicInfo(int id) {
        return materialNoPayDao.findRkdBasicInfo(id);
    }
  1. Dao层
    /**
     * 功能描述: 根据id查询入库单的基本信息(主表)
     * @author 码不多
     * @date 2021/9/7
     * @param id
     * @return com.buduo.entity.InitRkdBasicInfoModel 
     */
    InitRkdBasicInfoModel findRkdBasicInfo(@Param("id") int id);
	
	    /**
     * 功能描述: 查询对应入库单的子表物资(子表)
     * @author 码不多
     * @date 2021/9/5
     * @param materialId
     * @return java.util.List
     */
    List<Map> finRKDWuZiById(@Param("materialId") String materialId,@Param("materialName") String materialName);
  1. 用到的实体类
/**
 * @author 码不多
 * @version 1.0
 * @description: DB映射的主表实体
 * @date 2021/9/7 14:49
 */
@Data
@NoArgsConstructor
public class InitRkdBasicInfoModel{
    private int id; 
    private String rkd_number; 
    private String rkd_name;
    private String rkd_money;
    private String rkd_date;
    private String rkd_person;
    private String rkd_phone;
}
  1. sql映射文件
    <!-- 查主表 查询入库单基本信息(主表)通过id用于显示修改页面,这里我将它用到了导出查询-->
    <select id="findRkdBasicInfo" resultType="com.mabuduo.entity.InitRkdBasicInfoModel">
        select id,rkd_number,rkd_money,rkd_person,rkd_date,rkd_phone from rkd_mabuduo_info where id = #{id}
    </select>

    <!--查子表 分页查询入库单对应id的所有信息  这里我也将它用到了导出查询-->
    <select id="finRKDWuZiById" resultType="Map">
        select * from rkd_mabuduo_wuziinfo where
        <if test="materialName!=''">
            material_nameHeadPY like concat(concat("%",#{condition1}),"%") or
            material_code like concat(concat("%",#{condition1}),"%") or
            material_code like concat(concat("%",#{condition1}),"%") or
            material_name like concat(concat("%",#{condition1}),"%") or
            material_specifications like concat(concat("%",#{condition1}),"%") or
            material_source like concat(concat("%",#{condition1}),"%") or
            material_measurement like concat(concat("%",#{condition1}),"%") and
        </if>
        1=1 and rkd_mabuduo_wuziinfo.rkd_goodsId = #{materialId}
        ORDER BY id DESC
    </select>

这里注意 : 这里的sql,只需要根据你的业务sql去写查询就好了,我只是给提供了个想法,最简单粗暴的方式的 select * from 。。。。但是我相信你不会这么去做。

总结

其实导入,导出非常简单,无非就是利用POI去解析操作Excel,麻烦的地方就是Excel模板的格式,需要使用数字去找坐标定位,其实搞明白了API它们都是一样的东西。有问题可以评论,有时间的话,也会分享更多有用的文章 !

你可能感兴趣的:(java,poi,excel,vue,spring,boot)