文章目录

  • 前言
  • 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();ic=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样例

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

  1. 主表 :
  2. 子表 :

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

     <!-- 导入框,绑定了uploadfile()方法 --><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的依赖

        <!--03版本--><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>3.17-beta1</version></dependency><!--07版本--><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>3.17-beta1</version></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 intorkd_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*/@Overridepublic void importRKD(Map<String,String> map) {materialNoPayDao.importRKD(map);}/*** 功能描述:导入码不多入库单子表物资部分* @author 码不多* @date 2021/9/3* @param arrayList* @return void*/@Overridepublic 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: // BooleancellValue = 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")@ResponseBodypublic 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) 代码中要减1Row 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);}//定义变量提高作用域保存基本信息的自增idString rkdInfoId = null;//判断Map中是否有数据if (map.size()!=0){//添加到码不多入库单基本信息表中RKDService.importRKD(map);//获取其自增的idObject 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){//给其添加外键idlist.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

本文要导出的信息列表图

导出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放入,然后submitlet 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,集合长度也要相应+4for (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 */@Overridepublic 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}),"%") ormaterial_code like concat(concat("%",#{condition1}),"%") ormaterial_code like concat(concat("%",#{condition1}),"%") ormaterial_name like concat(concat("%",#{condition1}),"%") ormaterial_specifications like concat(concat("%",#{condition1}),"%") ormaterial_source like concat(concat("%",#{condition1}),"%") ormaterial_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它们都是一样的东西。有问题可以评论,有时间的话,也会分享更多有用的文章 !

SpringBoot+Vue+POI实现Excel的导入与导出相关推荐

  1. Vue前端实现excel的导入、导出、打印功能

    目录 一.相关依赖下载 二.excel导入功能 三.table导出excel表格 1.导出行数据 2.导出table数据(也会导出合并单元格) 3.导出二维数据的table数据 4.导出合并单元格ta ...

  2. POI实现excel的导入和导出

    03最多是65536  xls 对象HSSFWorkbook 07没有限制 xlsx 对象XSSFWorkbook 升级版SXSSFWorkbook 工作博 工作表sheet 行 列 从0开始 < ...

  3. SpringBoot通过WorkBook快速实现对Excel的导入和导出(包括数据校验)

    之前转载过一篇对Excel基本操作相关的文章,这篇文章的浏览量迅速飙升,以至于在我博客的热门文章中排到了第三的位置,不过那篇转载的文章实用性差并且讲解不是很清晰,所以打算趁着今天休息,写一篇关于Spr ...

  4. 蓄力-利用POI进行excel的导入导出(包含图片)

    这里写自定义目录标题 利用POI进行excel的导入导出 引入的jar包 excel导入 主方法: 将excel里面的图片转成数据 xls格式 xlsx格式 将图片数据转成字节流的方式传输到FTP服务 ...

  5. VUE+ElementUI生成Excel模板 导入数据生成表格(自适应)

    VUE+ElementUI生成Excel模板 导入数据生成表格(自适应) 最近项目需求,需要根据条件查询对应数据的参数(每条数据的参数名称和个数都不一样) ,生成Excel表格模板,再通过Excel模 ...

  6. 手摸手教学-利用原生POI对excel的导入导出以及阿里的easyexcel的基本操作

    文章目录 原生POI对excel的导入导出以及阿里的easyexcel的基本操作 首先是最原始的POI操作excel 其次是POI操作excel对数据库的导入导出 最后是阿里的easyexcel的简单 ...

  7. Vue实现在线编辑excel、导入、导出

    文章目录 概要 整体架构流程 小结 概要 Vue实现在线编辑excel.导入.导出 整体架构流程 luckysheet文档地址 exceljs文档地址 1.npm安装依赖 npm i exceljs ...

  8. Excel的导入与导出

    项目场景: 项目场景:Excel的导入与导出 环境配置: 创建项目,导入jar包,创建包,创建类,创建接口(不知道操作的可以看之前的文章) 配置web.xml <!-- 配置spring监听 - ...

  9. java如何导入和导出ex_SpringBoot中关于Excel的导入和导出

    前言 由于在最近的项目中使用Excel导入和导出较为频繁,以此篇博客作为记录,方便日后查阅.本文前台页面将使用layui,来演示对Excel文件导入和导出的效果.本文代码已上传至我的gitHub,欢迎 ...

最新文章

  1. LVM逻辑卷轴管理和磁盘配额实验
  2. python装饰器使用多吗_对于Python装饰器使用的一些建议
  3. HDU 3068 最长回文
  4. mybatis dao实现 || 接口代理方式实现
  5. 云服务器 自有操作系统,云服务器 自有操作系统
  6. JEEWX推出插件开发机制,现招募兴趣爱好者
  7. ubuntu安装jdk
  8. 敏捷开发团队管理系列之四:程序与测试团队III
  9. Java锁的升降级及synchoronized优化
  10. 总结搜索型手工注入的全过程
  11. GhostExp 2010特别版安装方法
  12. 什么是静态测试、动态测试、黑盒测试、白盒测试、α测试 β测试?
  13. 记录一次Tx_LCN连接失败的问题( There is no normal TM )
  14. 使用feed,欢迎使用http://feed.feedsky.com/xu_fan_blog订阅
  15. Appdata中local是文件,系统盘下的文件目录
  16. “好好说话,别伤人。”
  17. lzx和网页之间脚本交互调试方式
  18. 2020年技术研发岗薪酬排名
  19. 虚幻4: 蓝图入门与进阶
  20. 名悦集团:开车从不追尾,老司机分享驾驶避免事故小知识

热门文章

  1. 工作5-8年的外包测试员,平均年薪有多少?
  2. 思科路由器:网络故障诊断与排除命令
  3. 【山景BP1048使用记录】
  4. python低配贪食蛇
  5. Windows的一键安检脚本
  6. VM快照磁盘不能打开问题
  7. Julia常见符号意思
  8. 图片qq浏览器不显示,微信显示问题原因
  9. .tsv以及.csv格式文件
  10. 如何修改Docker镜像地址