1.正式な提供方法
easyexcel 公式文書記入用エクセル | イージーエクセル
公式のデモには、ローカルのテンプレート ファイルが含まれており、ローカルにダウンロードされます。
/**
* 复杂的填充
*
* @since 2.1.1
*/
@Test
public void complexFill() {
// 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
// {} 代表普通变量 {.} 代表是list的变量
String templateFileName =
TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "complex.xlsx";
String fileName = TestFileUtil.getPath() + "complexFill" + System.currentTimeMillis() + ".xlsx";
// 方案1
try (ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build()) {
WriteSheet writeSheet = EasyExcel.writerSheet().build();
// 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。
// forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用
// 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存
// 如果数据量大 list不是最后一行 参照下一个
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
excelWriter.fill(data(), fillConfig, writeSheet);
excelWriter.fill(data(), fillConfig, writeSheet);
Map<String, Object> map = MapUtils.newHashMap();
map.put("date", "2019年10月9日13:28:28");
map.put("total", 1000);
excelWriter.fill(map, writeSheet);
}
}
2. ビジネスシーンに応じた方法
Web プロジェクトを使用しており、出力を出力ストリーム OutputStream に変更しました
/**
* 导出质检任务详情
*
* @param id 任务主键,用于获取数据
*/
@Override
public void exportTask(Long id, HttpServletResponse response) {
ExcelWriter excelWriter = null;
try {
// outputStream:要导出的文件的输出流
OutputStream outputStream = response.getOutputStream();
// 模版文件
ClassPathResource classPathResource = new ClassPathResource("template/taskTemplate.xlsx");
// 使用模版文件的两种方式:
// 1、文件路径:.withTemplate(templateFileName)
// 2、输入流:.withTemplate(inputStream)
String templateFileName = classPathResource.getFile().getPath();
InputStream inputStream = classPathResource.getInputStream();
excelWriter = EasyExcel.write(outputStream).withTemplate(inputStream).excelType(ExcelTypeEnum.XLSX).autoCloseStream(Boolean.FALSE).build();
WriteSheet writeSheet = EasyExcel.writerSheet().build();
// 获取数据 dataList columnList formData
Map<String, Object> mapDetail = qmsQcTaskDetailService.getDataListByTaskId(id);
// 调用微服务获取字典 determine 用来翻译判定结果
List<SysDictItem> determineItem = remoteDictService.getDictByType("determine").getData();
// 调用微服务获取字典 product_unit 用来翻译零件单位
List<SysDictItem> unitItem = remoteDictService.getDictByType("product_unit").getData();
/* 1、List以外的数据 */
QmsQcTask formData = (QmsQcTask) mapDetail.get("formData");
determineItem.stream().filter(item -> StrUtil.equals(formData.getResult(), item.getItemValue())).findFirst().ifPresent(sysDictItem -> formData.setResult(sysDictItem.getLabel()));
unitItem.stream().filter(item -> StrUtil.equals(formData.getMaterialUnit(), item.getItemValue())).findFirst().ifPresent(sysDictItem -> formData.setMaterialUnit(sysDictItem.getLabel()));
excelWriter.fill(formData, writeSheet);
/* 2、List cols数据 */
List<Map<String, Object>> cols = new ArrayList<>();
// 查找抽检数是多少,即有几列
int num = formData.getSampleNum();
for (int i = 1; i <= num; i++) {
Map<String, Object> map = new HashMap<>();
// 列名
map.put("label", "" + i);
cols.add(map);
}
// 横向填充
FillConfig fillConfigCols = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build();
excelWriter.fill(new FillWrapper("cols", cols), fillConfigCols, writeSheet);
/* 3、List details数据 */
// 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。
// forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用
// 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
List<Map<String, Object>> dataList = (List<Map<String, Object>>) mapDetail.get("dataList");
for (int i = 0; i < dataList.size(); i++) {
Map<String, Object> data = dataList.get(i);
// 翻译判定合不合格(因为业务和字典不在一个数据库,所以放在代码里处理)
String determine = (String) data.get("determine");
determineItem.stream().filter(item -> StrUtil.equals(determine, item.getItemValue())).findFirst().ifPresent(sysDictItem -> data.put("determine", sysDictItem.getLabel()));
// 如果没有质检标准,每一个质检值都要转为合不合格
if (StrUtil.isBlank((String) data.get("inspection_standard"))){
int samplingNum = Optional.ofNullable((Integer) data.get("sampling_num")).orElse(0);
for (int j = 1; j <= samplingNum; j++) {
String val = (String) data.get(""+j);
int finalJ = j;
determineItem.stream().filter(item -> StrUtil.equals(val, item.getItemValue())).findFirst().ifPresent(sysDictItem -> data.put(""+ finalJ, sysDictItem.getLabel()));
}
}
// 给数据加上序号
data.put("index", i + 1);
}
excelWriter.fill(new FillWrapper("details", dataList), fillConfig, writeSheet);
// 设置输出流格式以及文件名:
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode("质检任务", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
// 千万别忘记close关闭流
if (excelWriter != null) {
excelWriter.close();
}
}
}
ステンシル:
効果:
3. 説明
1. 出力ストリームを取得する
// outputStream:要导出的文件的输出流
OutputStream outputStream = response.getOutputStream();
2.テンプレートファイルを入手する
// 模版文件
ClassPathResource classPathResource = new ClassPathResource("template/taskTemplate.xlsx");
// 方式一:路径
String templateFileName = classPathResource.getFile().getPath();
// 方式二:输入流
InputStream inputStream = classPathResource.getInputStream();
3. ExcelWriterとWriteSheetを作成する
excelWriter = EasyExcel.write(outputStream).withTemplate(inputStream).excelType(ExcelTypeEnum.XLSX).autoCloseStream(Boolean.FALSE).build();
WriteSheet writeSheet = EasyExcel.writerSheet().build();
テンプレート ファイルを使用するには 2 つの方法があります。いずれかを使用します。
1. ファイル パス: .withTemplate(templateFileName)
2. 入力ストリーム: .withTemplate(inputStream)
4. データを取得する
Map<String, Object> mapDetail = qmsQcTaskDetailService.getDataListByTaskId(id);
ここでメソッドを直接呼び出しました。マップには含まれています
dataList メイン リスト データ フォームリスト外のデータ データ
5.データを埋める
1. 辞書データを取得する
// 调用微服务获取字典 determine 用来翻译判定结果
List<SysDictItem> determineItem = remoteDictService.getDictByType("determine").getData();
// 调用微服务获取字典 product_unit 用来翻译零件单位
List<SysDictItem> unitItem = remoteDictService.getDictByType("product_unit").getData();
ビジネス データとディクショナリが同じデータベースにありません。SQL を変換できないため、コードで変換する必要があります。
2. リスト外のデータを埋める
/* 1、List以外的数据 */
QmsQcTask formData = (QmsQcTask) mapDetail.get("formData");
determineItem.stream().filter(item -> StrUtil.equals(formData.getResult(), item.getItemValue())).findFirst().ifPresent(sysDictItem -> formData.setResult(sysDictItem.getLabel()));
unitItem.stream().filter(item -> StrUtil.equals(formData.getMaterialUnit(), item.getItemValue())).findFirst().ifPresent(sysDictItem -> formData.setMaterialUnit(sysDictItem.getLabel()));
excelWriter.fill(formData, writeSheet);
テンプレートに書かれたオブジェクトの属性名とタグが分かれば
3. 少数の列で列名をパディングする
ステンシル:
効果:
列名の後半には行から列への変換が含まれ、列数が固定されていないため、上部の列名を横方向に埋める必要があります。リスト「cols」の名前が対応している必要があることに注意してください。
水平パディングを設定するには、.direction(WriteDirectionEnum.HORIZONTAL)を使用します。
/* 2、List cols数据 */
List<Map<String, Object>> cols = new ArrayList<>();
// 查找抽检数是多少,即有几列
int num = formData.getSampleNum();
for (int i = 1; i <= num; i++) {
Map<String, Object> map = new HashMap<>();
// 列名
map.put("label", "" + i);
cols.add(map);
}
// 横向填充
FillConfig fillConfigCols = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build();
excelWriter.fill(new FillWrapper("cols", cols), fillConfigCols, writeSheet);
4. メイン リスト データを入力する
/* 3、List details数据 */
// 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。
// forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用
// 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
List<Map<String, Object>> dataList = (List<Map<String, Object>>) mapDetail.get("dataList");
/*以下为个人业务处理*/
for (int i = 0; i < dataList.size(); i++) {
Map<String, Object> data = dataList.get(i);
// 翻译判定合不合格(因为业务和字典不在一个数据库,所以放在代码里处理)
String determine = (String) data.get("determine");
determineItem.stream().filter(item -> StrUtil.equals(determine, item.getItemValue())).findFirst().ifPresent(sysDictItem -> data.put("determine", sysDictItem.getLabel()));
// 如果没有质检标准,每一个质检值都要转为合不合格
if (StrUtil.isBlank((String) data.get("inspection_standard"))){
int samplingNum = Optional.ofNullable((Integer) data.get("sampling_num")).orElse(0);
for (int j = 1; j <= samplingNum; j++) {
String val = (String) data.get(""+j);
int finalJ = j;
determineItem.stream().filter(item -> StrUtil.equals(val, item.getItemValue())).findFirst().ifPresent(sysDictItem -> data.put(""+ finalJ, sysDictItem.getLabel()));
}
}
// 给数据加上序号
data.put("index", i + 1);
}
/*以上为个人业务处理*/
excelWriter.fill(new FillWrapper("details", dataList), fillConfig, writeSheet);
最も重要な 3 つの文:
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
List<Map<String, Object>> dataList = (List<Map<String, Object>>) mapDetail.get("dataList");
excelWriter.fill(new FillWrapper("details", dataList), fillConfig, writeSheet);
ここで、入力パラメーターは forceNewRow を使用することに注意してください。これは、リストに書き込むときに、リストの下に空白行があるかどうかに関係なく行が作成され、その後のデータが後方に移動することを意味します。デフォルトは false で、次の行が直接使用されます。存在しない場合は作成されます。 forceNewRow を true に設定すると、すべてのデータをメモリに格納してしまうという欠点があるため、注意して使用してください. 簡単に言えば、テンプレートにリストがあり、リストが最後の行ではない場合は、それを設定する必要があります .下に入力するデータがまだある場合は forceNewRow=true ただし、これによりすべてのデータがメモリに格納され、多くのメモリが消費されます。
5. 出力ストリーム形式とファイル名を設定する
// 设置输出流格式以及文件名:
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode("质检任务", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
4. 別の考え
私のテンプレートは動的な列を使用していますが、列の数がわからないため、100 を超える列を書きました。
別のアイデアがあります:
リスト外のデータが最初に埋められた場合, 次にメインリストの列が埋められ, 次にメインリストのマークが書き込まれます. これらの操作が完了した後, ファイルは別の出力ストリームに出力されます. stream を入力ストリームに変換し、メインリストにデータを埋めてレスポンスに出力します。
理論的には可能ですが、試したことはありません。