Java將對象寫入Excel文件

本文通過poi依賴實現將java對象寫入excel之中,核心理念是通過反射獲取Java對象的getter方法和屬性,通過getter方法獲取要寫入excel中的值,再通過屬性上的自定義註解獲取excel標題行,然後以文件流的方式寫入excel。話不多說,直接上碼。

首先我們定義註解@ExcelTitle,用來初始化excel第一行作爲標題行

/**
 * excel標題頭註解,當Java屬性沒有使用此註解,則默認使用Java屬性作爲標題。
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelTitle {
  String value();
}

然後定義對象轉excel工具類ExcelWriter


@Slf4j
public class ExcelWriter {

  private static ThreadLocal<ExcelValueFormatter> valueFormatter = ThreadLocal
      .withInitial(() -> new DateValueFormatter("yyyy-MM-dd"));

  public static void setExcelValueFormatter(ExcelValueFormatter excelValueFormatter) {
    valueFormatter.set(excelValueFormatter);
  }

  public static <E> void writeToExcel(List<E> list, Class<E> clazz, String fileName)
      throws InvocationTargetException, IllegalAccessException {
    @SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
    List<Object[]> dataList = new ArrayList<>();
    Map<String, Method> fieldMethodMap = buildFieldMethodMap(clazz);
    Map<String, String> fieldTitleMap = buildFieldTitleMap(clazz, fieldMethodMap);
    List<Entry<String, Method>> methodEntrySet = new ArrayList<>(fieldMethodMap.entrySet());
    int addMark = 0;
    int itemSize = fieldTitleMap.size();
    String[] titleArr = new String[itemSize];
    for (E obj : list) {
      Object[] item = new Object[itemSize];
      for (int i = 0; i < methodEntrySet.size(); i++) {
        Entry<String, Method> methodEntry = methodEntrySet.get(i);
        String field = methodEntry.getKey();
        if (addMark < itemSize) {
          titleArr[addMark] = fieldTitleMap.get(field);
          addMark++;
        }
        Method method = methodEntry.getValue();
        Object value = formatValue(method, obj, valueFormatter.get());
        if (value != null) {
          item[i] = value;
        }
      }
      dataList.add(item);
    }
    writeObjectToExcel(dataList, titleArr, fileName);
  }

  private static Object formatValue(Method method, Object obj,
      ExcelValueFormatter excelValueFormatter)
      throws InvocationTargetException, IllegalAccessException {
    Object value = method.invoke(obj);
    if (value == null) {
      return null;
    }
    if(excelValueFormatter == null) {
      return value;
    }
    Class<?> returnType = method.getReturnType();
    return excelValueFormatter.formatValue(returnType, value);
  }

  private static <E> Map<String, Method> buildFieldMethodMap(Class<E> clazz) {
    List<Method> getMethods = Arrays.stream(clazz.getMethods())
        .filter(
            method -> method.getName().startsWith("get") && !method.getName().equals("getClass"))
        .collect(
            Collectors.toList());
    Map<String, Method> fieldMethodMap = new LinkedHashMap<>();
    for (Method getMethod : getMethods) {
      String m = getMethod.getName().replace("get", "");
      String field = m.substring(0, 1).toLowerCase() + m.substring(1);
      fieldMethodMap.put(field, getMethod);
    }
    return fieldMethodMap;
  }

  public static <E> Field[] getAllFields(Class<E> clazz){
    List<Field> fieldList = new ArrayList<>();
    while (clazz != null){
      fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
      clazz = (Class<E>) clazz.getSuperclass();
    }
    Field[] fields = new Field[fieldList.size()];
    fieldList.toArray(fields);
    return fields;
  }

  private static <E> Map<String, String> buildFieldTitleMap(Class<E> clazz,
      Map<String, Method> fieldMethodMap) {
    Map<String, String> fieldTitleMap = new LinkedHashMap<>();
    Field[] fields = getAllFields(clazz);
    Arrays.stream(fields).forEach(field -> {
      if (fieldMethodMap.containsKey(field.getName())) {
        ExcelTitle excelTitle = field.getAnnotation(ExcelTitle.class);
        String title = excelTitle == null ? field.getName() : excelTitle.value();
        fieldTitleMap.put(field.getName(), title);
      }
    });
    return fieldTitleMap;
  }

  private static void writeObjectToExcel(List<Object[]> list, String[]
      excelTitle, String fileName) {
    //在內存中創建Excel文件
    Workbook workbook;
    if (fileName.endsWith("xls")) {
      workbook = new HSSFWorkbook();
    } else if (fileName.endsWith("xlsx")) {
      workbook = new XSSFWorkbook();
    } else {
      throw new IllegalArgumentException("fileName not legal");
    }
    Sheet sheet = workbook.createSheet();
    //標題行
    Row titleRow = sheet.createRow(0);
    for (int i = 0; i < excelTitle.length; i++) {
      titleRow.createCell(i).setCellValue(excelTitle[i]);
    }
    //創建數據行並寫入值
    for (Object[] dataArr : list) {
      int lastRowNum = sheet.getLastRowNum();
      Row dataRow = sheet.createRow(lastRowNum + 1);
      for (int i = 0; i < dataArr.length; i++) {
        Cell cell = dataRow.createCell(i);
        Object cellValue = dataArr[i];
        if(cellValue != null) {
          setCellValue(cellValue, cell);
        }
      }
    }
    //創建輸出流對象
    FileOutputStream outputStream = null;
    try {
      outputStream = new FileOutputStream(new File(fileName));
    } catch (FileNotFoundException e) {
      log.error("file not found", e);
    }
    try {
      workbook.write(outputStream);
    } catch (IOException e) {
      log.error("write to file failed", e);
    } finally {
      if (outputStream != null) {
        try {
          outputStream.close();
        } catch (IOException ignore) {
        }
      }
    }
  }

  private static void setCellValue(Object cellValue, Cell cell) {
    if (cellValue instanceof Boolean) {
      cell.setCellValue((boolean) cellValue);
    } else if (cellValue instanceof String) {
      cell.setCellValue(cellValue.toString());
    } else if (cellValue instanceof Double || cellValue instanceof Integer
        || cellValue instanceof Long) {
      cell.setCellValue(Double.valueOf(cellValue.toString()));
    } else if (cellValue instanceof Date) {
      cell.setCellValue((Date) cellValue);
    } else if (cellValue instanceof Calendar) {
      cell.setCellValue((Calendar) cellValue);
    } else if (cellValue instanceof RichTextString) {
      cell.setCellValue((RichTextString) cellValue);
    } else {
      cell.setCellValue(cellValue.toString());
    }
  }
}

這個工具類涉及到一個自定義的接口ExcelValueFormatter,它用來實現將不同類型的java屬性映射到不同的excel單元格格式。由於ExcelValueFormatter是個接口,所以你可以實現它,自定義不同的映射策略。

public interface ExcelValueFormatter {
  Object formatValue(Class<?> returnType, Object value);
}

本案例提供一個默認的實現類DateValueFormatter,將Date類型的屬性轉換爲格式爲yyyy-MM-dd的文本。

@Data
@AllArgsConstructor
public class DateValueFormatter implements ExcelValueFormatter {

  private String dateFormat;

  @Override
  public Object formatValue(Class<?> returnType, Object value) {
    if (returnType.equals(Date.class)) {
      return DateTimeFormatter.ofPattern(dateFormat)
          .format(toLocalDateTime((Date) value));
    } else {
      return value;
    }
  }

  private static LocalDateTime toLocalDateTime(Date date) {
    Instant instant = date.toInstant();
    ZoneId zoneId = ZoneId.systemDefault();
    return instant.atZone(zoneId).toLocalDateTime();
  }
}

代碼用到了lombok,還需要log4j,slf4j,總體依賴如下:

    <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi-ooxml</artifactId>
      <version>3.17</version>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.8</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.25</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.25</version>
    </dependency>

測試代碼:

    public static void main(String[] args) {
        try {
            List<Student> students = new ArrayList<>();
            for (int i = 0; i < 100; i++) {
                students.add(new Student(i, "member" + i, i * 55D, new Date()));
            }
            ExcelWriter.writeToExcel(students, Student.class, "/Users/zhuhuiyuan/Downloads/2.xlsx");
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    @Data
    @AllArgsConstructor
    public static class Student {
        @ExcelTitle("id")
        private Integer id;

        @ExcelTitle("姓名")
        private String name;

        @ExcelTitle("薪水")
        private Double salary;

        @ExcelTitle("生日")
        private Date birthDay;
    }

測試結果:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章