Giter VIP home page Giter VIP logo

easy-excel's Introduction

快速导出报表工具

造个轮子,便于导入和导出对应的excel报表。

maven

注意: 0.2.0版本与之前的并不相兼容

<dependency>
  <groupId>io.github.mrdear</groupId>
  <artifactId>excel</artifactId>
  <version>0.2.1</version>
</dependency>

核心类

  • EasyDoc : 入口类,所有对外的操作都是由该类发起,主要有export与read两个操作。
  • DocWriter:导出类,其方法分为非终端操作与终端操作,终端操作会输出并关闭该流,非终端操作则可以继续接着读取,应对一张excel中含有多个sheet的情况。
  • DocReader:读取类,与上述ExcelWriter一样的操作。
  • DocField:修饰实体类注解,Excel中最麻烦的是header,因此提倡每一张报表单独对应一个POJO类,使用注解标识相应字段。
  • DocIgnore: 修饰字段,可以指定忽略某一字段
  • WriteContext:针对导出过程中一张sheet的配置,使用Builder模式构建。
  • ReadContext:针对读取过程中一张sheet的配置,使用Builder模式构建。

Example

实体类 实体类使用注解标识字段,不使用的话则属性名会作为对应的columnName

public class UserWithAnnotation {

  @DocField(columnName = "用户名")
  private String username;

  @DocField(columnName = "用户密码")
  private String passwd;

  @DocField(columnName = "登录日期", convert = YmdDateConverter.class)
  private Date date;
}

单张表

export

 @Test
  public void testSimpleWithAnnotationExport() {
    List<UserWithAnnotation> users = mockUserWithAnnotation(5);
      try (DocWriter writer = EasyDoc.export(DocType.XLSX, currentPath + "/testSimpleWithAnnotationExport.xlsx")) {
          writer
              .export(WriteContextBuilder.builder()
                  .dataSource(users)
                  .buildForExcel("user"))
              .writeAndFlush();
      } catch (IOException e) {
          e.printStackTrace();
      }
  }

import

  @Test
   public void testReadWithAnnotation() throws Exception {
     InputStream inputStream = DocReaderTest.class
         .getClassLoader().getResourceAsStream("testReadWithAnnotation.xlsx");
 
     try (DocReader reader = EasyDoc.read(DocType.XLSX, inputStream)){
       List<UserWithAnnotation> result = reader.resolve(ReadContextBuilder.<UserWithAnnotation>builder()
           .clazz(UserWithAnnotation.class)
           .buildForExcel())
           .getData();
 
       Assert.assertEquals(result.size(), 5);
       Assert.assertEquals(result.get(0).getPasswd(), "0b6df627-5975-417b-abc9-1f2bad5ca1e2");
       Assert.assertEquals(result.get(1).getUsername(), "张三1");
       Assert.assertNull(result.get(0).getNickName());
     }
 
   }

多张表+自定义header

sheet1最顶部有自定义的title

sheet2为普通表格

由于自定义的title往往非常复杂且多变,很难做到通用,因此这里是直接抛出一个钩子,可以自己实现自己想要的任何操作。

export

    @Test
    public void testCustomMoreSheetExport() {
        List<UserWithAnnotation> users = mockUserWithAnnotation(5);

        String fullFileName = currentPath + "/testCustomMoreSheetExport.xlsx";

        try (DocWriter writer = EasyDoc.export(DocType.XLSX, fullFileName)){

            writer.export(WriteContextBuilder.builder()
                    .dataSource(users)
                    .createSheetHook((sheet, context) -> {
                        Row row = sheet.createRow(0);
                        sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 2));
                        Cell cell = row.createCell(0);
                        cell.setCellValue("custom header");
                    })
                    .startRow(1)
                    .buildForExcel("user1"))
                .export(WriteContextBuilder.builder()
                    .dataSource(users)
                    .buildForExcel("user2"))
                .writeAndFlush();
        } catch (IOException e) {
            e.printStackTrace();
        }

        try (DocReader reader = EasyDoc.read(fullFileName)) {
            ReaderResolveResult<UserWithAnnotation> user = reader.resolve(ReadContextBuilder
                .<UserWithAnnotation>builder()
                .clazz(UserWithAnnotation.class)
                .headerStart(1)
                .buildForExcel(0)
            );

            Assert.assertEquals(5, user.size());
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

import

  @Test
  public void testCustomExcel() {
    InputStream inputStream = DocReaderTest.class
        .getClassLoader().getResourceAsStream("testCustomExcel.xlsx");
    DocReader reader = EasyDoc.read(DocType.XLSX, inputStream);

    List<UserWithAnnotation> sheet1Result = reader.resolve(ReadContextBuilder.<UserWithAnnotation>builder()
        .clazz(UserWithAnnotation.class)
        .headerStart(1)
        .excelReadSheetHook((sheet, context) -> {
          Row row = sheet.getRow(0);
          Assert.assertEquals(row.getCell(0).getStringCellValue(), "custom header");
        })
        .buildForExcel(0))
        .getData();

    Assert.assertEquals(sheet1Result.size(), 5);
    Assert.assertEquals(sheet1Result.get(1).getUsername(), "张三1");


    List<UserWithAnnotation> sheet2Result = reader.resolve(ReadContextBuilder.<UserWithAnnotation>builder()
        .clazz(UserWithAnnotation.class)
        .buildForExcel(1))
        .getData();

    Assert.assertEquals(sheet2Result.size(), 5);
    Assert.assertEquals(sheet2Result.get(1).getUsername(), "张三1");

  }

写入HttpServletResponse

提供ResponseHelperHttpServletResponse获取对应的输出流,然后放入

OutputStream outputStream = ResponseHelper.wrapper(response, "order.xlsx");
EasyExcel.export(outputStream)....

easy-excel's People

Contributors

dependabot[bot] avatar mrdear avatar rxliuli avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

easy-excel's Issues

尝试了一下还是想提一些问题

尝试了一下还是想提一些问题

如果需要,吾辈可以修改后 Pull Request

可否在查找 Bean 字段的时候递归向上查找

再很多时候,吾辈这边的实体类与 VO 是分离的父子类,一般使用 VO 作为数据承载,而实体类仅与数据库对应,那么能否在查找字段时递归向上查找直到 Object/Serializable 或是全局指定的某个基类?

递归获取字段你可以使用 SuperClassRefUtil

转换器可否默认使用

像 Date 一般而言确实应该转换为 yyyy-MM-dd HH:mm:ss 这种人类可读的格式,如果不指定的话就默认使用它就好了,总好过导出的是 43559.4816650926 这种不知道什么鬼的东西。总而言之,约定优于配置应该是最好的!

可否增加对 Java8 时间的支持

现在都已经 2019 了,Java8 已经这么久还不打算尝试新的时间 API 么?类似于 LocalDateTimeLocalDateLocalTime 这些常见的时间最好也支持一下吧

目前默认 toStringT

LocalDateTime 应该可以使用下列方式导出

LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

导出的列可否自适应宽度

现在不管数据有多长,宽度总是一样的,能否设置一下 POI 的列样式为自适应宽度?调用一下 Sheet.autoSizeColumn 即可

可指定列顺序

目前的列顺序似乎依赖于 getDeclaredFields 获得的数组,能否在 ExcelField 中添加一个 sort 属性指定顺序呢(只要做个排序即可)

Stream 排序如下

/**
 * 根据元素的指定唯一标识过滤集合的 lambda 函数
 *
 * @param keyExtractor 元素标识生成函数
 * @param <T>          集合元素的类型
 * @param <K>          元素标识的类型
 * @return {@link Predicate<T>} 类型的函数
 */
public static <T, K> Predicate<T> distinctByKey(Function<? super T, K> keyExtractor) {
    final Map<K, Boolean> seen = new ConcurrentHashMap<>(16);
    return object -> seen.putIfAbsent(keyExtractor.apply(object), Boolean.TRUE) == null;
}

导入时如果出现错误可否告知是哪一行哪一列的错误而非直接抛出原始异常

像是导出时 Date 默认转换得到的 43559.4816651042 这种数据,在导入时就会出现错误,然而并不知道哪一行哪一列错了。。。

java.lang.IllegalArgumentException: Can not set java.util.Date field io.github.mrdear.excel.model.UserInfoVo.date to java.lang.String

添加开源协议

如果希望别人使用最好添加开源协议,例如 MIT,不然在使用后总要担心你突然变成 GPL 之类的协议。而且,没有 License 真的显得很不专业

默认不包含没有注解的字段

默认使用英文字段名导出,你是认真的?讲道理很多使用者并不一定懂英文,更懒得去学这些。所以,还是默认不包含字段比较好点吧?另外,Vo 里面常常包含了很多用不到的字段呢!例如子表的 List 什么的,这些导出恐怕有点困难吧


未完待续。。。

StringToDateConvert 时间格式写错了

package io.github.mrdear.excel.domain.convert;

import io.github.mrdear.excel.ExcelException;
import java.text.ParseException;
import java.util.Date;
import java.util.function.Function;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.FastDateFormat;

public class StringToDateConvert implements Function<String, Date> {
    private static FastDateFormat YMDHMS_ = FastDateFormat.getInstance("yyyy-MM-dd hh:MM:ss");

    public StringToDateConvert() {
    }

    public Date apply(String s) {
        if (StringUtils.isEmpty(s)) {
            return null;
        } else {
            try {
                return YMDHMS_.parse(s);
            } catch (ParseException var3) {
                throw new ExcelException(var3);
            }
        }
    }
}

yyyy-MM-dd hh:MM:ss 写错了,正确的是 yyyy-MM-dd HH:mm:ss。

我一开始还以为是xls有问题,稍微浪费了一点时间

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.