应用 POI 解析 Excel 表格并批量上传到数据库

曾经终败给现在 2023-10-11 15:30 147阅读 0赞

  在日常生活中,我们时常会遇到添加用户之类的操作,但是这类操作,只能一位一位的添加。遇到向我这种强迫症晚期患者,会被烦死… 那么应用 POI 解析含有用户信息的 EXCEL 表格会省很多时间。本文针对解析 EXCEL 表格以及将表格上的内容批量上传到数据库提供一项实用的解决方案。
在这里插入图片描述


目录

    • 一、导入 pom 坐标
    • 二、编写解析 Excel 表格的工具类
    • 三、批量上传至服务器
    • 四、整体代码
    • 五、整体效果演示
    • 六、补充

一、导入 pom 坐标

  1. <!--解析excel-->
  2. <dependency>
  3. <groupId>org.apache.poi</groupId>
  4. <artifactId>poi-ooxml</artifactId>
  5. <version>3.9</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.apache.poi</groupId>
  9. <artifactId>poi</artifactId>
  10. <version>3.9</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>joda-time</groupId>
  14. <artifactId>joda-time</artifactId>
  15. <version>2.10.1</version>
  16. </dependency>

二、编写解析 Excel 表格的工具类

  创建 ParseExcelUtil 工具类,编写解析指定对象表格的静态方法。方法传入的参数分别是上传文件的路径,以及文件名称。


为解析 EXCEL 做准备工作:

  1. 获取解析文件的输入流。
  2. 根据文件类型(xls、xlsx)创建一个工作簿。(这样两种xls、xlsx后缀的表格都会被解析)
  3. 获取的第一个sheet。(可根据索引检索指定的sheet)
  4. 设置存储 list 集合。

对 EXCEL 进行解析流程如下:
在这里插入图片描述


  1. import org.apache.poi.hssf.usermodel.HSSFCell;
  2. import org.apache.poi.hssf.usermodel.HSSFDateUtil;
  3. import org.apache.poi.hssf.usermodel.HSSFWorkbook;
  4. import org.apache.poi.ss.usermodel.Cell;
  5. import org.apache.poi.ss.usermodel.Row;
  6. import org.apache.poi.ss.usermodel.Sheet;
  7. import org.apache.poi.ss.usermodel.Workbook;
  8. import org.apache.poi.xssf.usermodel.XSSFWorkbook;
  9. import java.io.File;
  10. import java.io.FileInputStream;
  11. import java.io.IOException;
  12. import java.io.InputStream;
  13. import java.text.DateFormat;
  14. import java.text.ParseException;
  15. import java.text.SimpleDateFormat;
  16. import java.util.ArrayList;
  17. import java.util.Date;
  18. import java.util.List;
  19. public class ParseExcelUtil {
  20. private static InputStream in;
  21. private static Workbook workbook;
  22. private static Sheet sheet;
  23. public static List<Coach> parseCoachExcel(String path, String fileName) throws IOException, ParseException {
  24. //1.获取解析文件的输入流
  25. in = new FileInputStream(path+fileName);
  26. //2. 根据文件类型(xls、xlsx)创建一个工作簿,使用excel能操作的这边他都可以操作
  27. if(fileName.contains("xlsx")){
  28. workbook = new XSSFWorkbook(in);
  29. }else{
  30. workbook = new HSSFWorkbook(in);
  31. }
  32. //3. 得到表,第一个sheet
  33. sheet = workbook.getSheetAt(0);
  34. //设置存储list
  35. List<Coach> list = new ArrayList<Coach>();
  36. //这里对第一行标题不解析,如果要解析,那么就需要将索引值改写为0
  37. for(int rowIndex = 1; rowIndex < sheet.getPhysicalNumberOfRows(); rowIndex ++){
  38. //4. 得到行
  39. Row row = sheet.getRow(rowIndex);
  40. if (row == null){
  41. //非空判断
  42. continue;
  43. }
  44. Coach coach = new Coach();//创建coach对象
  45. for(int cellIndex = 0; cellIndex < row.getPhysicalNumberOfCells(); cellIndex ++){
  46. //5. 得到列
  47. Cell cell = row.getCell(cellIndex);
  48. if(cell==null){
  49. continue;
  50. }
  51. int cellType = cell.getCellType();
  52. String cellValue = "";
  53. switch (cellType){
  54. case HSSFCell.CELL_TYPE_STRING: //字符串
  55. cellValue = cell.getStringCellValue();
  56. break;
  57. case HSSFCell.CELL_TYPE_BOOLEAN: //布尔
  58. cellValue = String.valueOf(cell.getBooleanCellValue());
  59. break;
  60. case HSSFCell.CELL_TYPE_BLANK: //空
  61. System.out.print("空!!");
  62. break;
  63. case HSSFCell.CELL_TYPE_NUMERIC: //数字(日期、普通数字)
  64. if(HSSFDateUtil.isCellDateFormatted(cell)){
  65. //日期
  66. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
  67. Date date = HSSFDateUtil.getJavaDate(cell.getNumericCellValue());
  68. cellValue = sdf.format(date);
  69. }else{
  70. //普通数字
  71. cell.setCellType(HSSFCell.CELL_TYPE_STRING);
  72. cellValue = cell.toString();
  73. }
  74. break;
  75. case HSSFCell.CELL_TYPE_ERROR: //错误
  76. cellValue = "错误";
  77. break;
  78. }
  79. DateFormat format=new SimpleDateFormat("yyyy-MM-dd");
  80. switch (cellIndex){
  81. case 0://number
  82. coach.setNumber((String) cellValue);
  83. break;
  84. case 1://name
  85. coach.setName((String)cellValue);
  86. break;
  87. case 2://sex
  88. coach.setSex((String)cellValue);
  89. break;
  90. case 3://courseNumber
  91. coach.setCourseNumber((String)cellValue);
  92. break;
  93. case 4://birthday
  94. coach.setBirthday(format.parse(cellValue));
  95. break;
  96. case 5://phone
  97. coach.setPhone((String)cellValue);
  98. break;
  99. case 6://ResponsibleClass
  100. coach.setResponsibleClass((String)cellValue);
  101. break;
  102. case 7://registrationDate
  103. coach.setRegistrationDate(format.parse(cellValue));
  104. break;
  105. case 8://effectiveDeadline
  106. coach.setEffectiveDeadline(format.parse(cellValue));
  107. break;
  108. }
  109. }
  110. list.add(coach);
  111. }
  112. //输入流关闭
  113. in.close();
  114. //上传文件删除
  115. File fileDelete = new File(path+fileName);
  116. fileDelete.delete();
  117. return list;
  118. }
  119. }

三、批量上传至服务器

Dao 层:

  1. @Insert("<script>" +
  2. "insert into coach(id, number, name, sex, courseNumber, birthday, phone, responsibleClass, registrationDate, effectiveDeadline) VALUES " +
  3. "<foreach collection='list' item='item' index='index' separator=','> " +
  4. "(null,#{item.number},#{item.name}, #{item.sex}, #{item.courseNumber}, #{item.birthday}, #{item.phone}, #{item.responsibleClass}, #{item.registrationDate}, #{item.effectiveDeadline}) " +
  5. "</foreach>" +
  6. "</script>")
  7. void insertCoachOfBatch(List<Coach> coachList);

注:这里以个人项目中的 Coach 实体类对象进行举例,后文会给出 Coach 对象的实体类代码,读者做参考即可。


四、整体代码

前端 jsp:

  1. <!--文件上传部分-->
  2. <div class="modal fade" id="myModal-upload" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" data-backdrop="false">
  3. <div class="modal-dialog" role="document" id="uploadPopup">
  4. <div class="modal-content">
  5. <div class="modal-header">
  6. <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
  7. <h4 class="modal-title" id="myModalLabel-4">批量导入数据</h4>
  8. </div>
  9. <form class="form-horizontal" id="upload-form" action="${pageContext.request.contextPath}/admin/coach/upload" enctype="multipart/form-data" method="post">
  10. <div class="form-group">
  11. <label for="chooseFileUpload" class="col-sm-3 control-label" >选择文件</label>
  12. <div class="col-sm-9" >
  13. <input type="file" class="" id="chooseFileUpload" placeholder="" name="upload" />
  14. </div>
  15. </div>
  16. <div class="form-group">
  17. <div class="col-xs-offset-4 col-sm-offset-4 col-xs-7 col-sm-7" style="color: red; text-align: right;">
  18. 注意:指定文件格式!
  19. </div>
  20. </div>
  21. </form>
  22. <div class="modal-footer">
  23. <button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span> 关闭</button>
  24. <button type="button" id="btn_submit_upload" class="btn btn-primary" data-dismiss="modal"> 上传</button>
  25. </div>
  26. </div>
  27. </div>
  28. </div>
  29. <script>
  30. $(function (){
  31. //文件上传
  32. $('#uploadBtn').click(function () {
  33. $('#myModal-upload').modal();
  34. });
  35. $('#btn_submit_upload').click(function () {
  36. $('#upload-form').submit();
  37. });
  38. })
  39. </script>

页面效果如下:
在这里插入图片描述


Controller层:

  1. @Controller
  2. @RequestMapping("/admin/coach")
  3. public class CoachController {
  4. @Autowired
  5. private CoachService coachService;
  6. @RequestMapping(path = "/upload")
  7. public String fileUpload(HttpServletRequest request, MultipartFile upload) throws Exception {
  8. //使用 fileupload 组件完成文件上传
  9. String path = request.getSession().getServletContext().getRealPath("/upload/");//文件的上传位置
  10. //判断该路径是否存在
  11. File file = new File(path);
  12. if(!file.exists()){
  13. //创建该文件夹
  14. file.mkdirs();
  15. }
  16. //说明上传文件项
  17. //获取上传文件的名称
  18. String fileName = upload.getOriginalFilename();
  19. //完成文件上传
  20. upload.transferTo(new File(path, fileName));
  21. coachService.parsingExcel(path, fileName);
  22. return "admin/fileUploadSuccess";
  23. }
  24. }

Service层:

  1. @Service("coachService")
  2. public class CoachServiceImpl implements CoachService {
  3. @Autowired
  4. private CoachDao coachDao;
  5. @Override
  6. public void parsingExcel(String path, String fileName) throws IOException, ParseException {
  7. List<Coach> coachList = ParseExcelUtil.parseCoachExcel(path, fileName);
  8. coachDao.insertCoachOfBatch(coachList);
  9. }
  10. }

Coach实体类 :

  1. import com.fasterxml.jackson.annotation.JsonFormat;
  2. import org.springframework.format.annotation.DateTimeFormat;
  3. import java.io.Serializable;
  4. import java.util.Date;
  5. public class Coach implements Serializable {
  6. private static final long serialVersionUID = 1L;
  7. private Integer id;
  8. private String number;
  9. private String name;
  10. private String sex;
  11. private String courseNumber;
  12. private String phone;
  13. private String responsibleClass;
  14. @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
  15. @DateTimeFormat(pattern = "yyyy-MM-dd")
  16. private Date birthday;
  17. @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
  18. @DateTimeFormat(pattern = "yyyy-MM-dd") //添加数据的时候防止格式不符
  19. private Date registrationDate;
  20. @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
  21. @DateTimeFormat(pattern = "yyyy-MM-dd")
  22. private Date effectiveDeadline;
  23. //一对一关联
  24. private Course course;
  25. public static long getSerialVersionUID() {
  26. return serialVersionUID;
  27. }
  28. public Integer getId() {
  29. return id;
  30. }
  31. public void setId(Integer id) {
  32. this.id = id;
  33. }
  34. public String getNumber() {
  35. return number;
  36. }
  37. public void setNumber(String number) {
  38. this.number = number;
  39. }
  40. public String getName() {
  41. return name;
  42. }
  43. public void setName(String name) {
  44. this.name = name;
  45. }
  46. public String getSex() {
  47. return sex;
  48. }
  49. public void setSex(String sex) {
  50. this.sex = sex;
  51. }
  52. public String getCourseNumber() {
  53. return courseNumber;
  54. }
  55. public void setCourseNumber(String courseNumber) {
  56. this.courseNumber = courseNumber;
  57. }
  58. public String getPhone() {
  59. return phone;
  60. }
  61. public void setPhone(String phone) {
  62. this.phone = phone;
  63. }
  64. public String getResponsibleClass() {
  65. return responsibleClass;
  66. }
  67. public void setResponsibleClass(String responsibleClass) {
  68. this.responsibleClass = responsibleClass;
  69. }
  70. public Date getBirthday() {
  71. return birthday;
  72. }
  73. public void setBirthday(Date birthday) {
  74. this.birthday = birthday;
  75. }
  76. public Date getRegistrationDate() {
  77. return registrationDate;
  78. }
  79. public void setRegistrationDate(Date registrationDate) {
  80. this.registrationDate = registrationDate;
  81. }
  82. public Date getEffectiveDeadline() {
  83. return effectiveDeadline;
  84. }
  85. public void setEffectiveDeadline(Date effectiveDeadline) {
  86. this.effectiveDeadline = effectiveDeadline;
  87. }
  88. public Course getCourse() {
  89. return course;
  90. }
  91. public void setCourse(Course course) {
  92. this.course = course;
  93. }
  94. @Override
  95. public String toString() {
  96. return "Coach{" +
  97. "id=" + id +
  98. ", number='" + number + '\'' +
  99. ", name='" + name + '\'' +
  100. ", sex='" + sex + '\'' +
  101. ", courseNumber='" + courseNumber + '\'' +
  102. ", phone='" + phone + '\'' +
  103. ", responsibleClass='" + responsibleClass + '\'' +
  104. ", birthday=" + birthday +
  105. ", registrationDate=" + registrationDate +
  106. ", effectiveDeadline=" + effectiveDeadline +
  107. ", course=" + course +
  108. '}';
  109. }
  110. }

五、整体效果演示

在这里插入图片描述


六、补充

  当我们在上传文件的时候,对日期类型解析会失败。因为 POI 解析的是指定格式的表格,所以对日期字符串格式识别不出,因此需要对日期字段的格式进行指定类型为日期。

在这里插入图片描述


  对 POI 解析 EXCEL 表格的相关操作,就先解析到这里。POI 解析EXCEL存在一定的局限性,比如要将数据所对应的列提前指定等。 如果读者还对 POI 解析 EXCEL 表格操作中出现问题,欢迎大家在评论区留言哦~

发表评论

表情:
评论列表 (有 0 条评论,147人围观)

还没有评论,来说两句吧...

相关阅读

    相关 java excel--poi

    工作中很多批量上传需求不同,每个需求都要写一次批量上传代码,太烦。。决定写一个通用工具,此代码复制即用。需要改进的地方请评论区留言 方法调用传参例子: 上传文件中列的字段