对接在线支付,部分平台的在线支付账单是直接一个zip文件包含着若干csv格式的excel文件,这部分如果想对账什么的还需要把数据提取出来储存到数据库,这里不涉及在线下载,直接用一个本地下载好的支付宝zip账单文件做调试。
zip文件:
使用postman调用接口:
{
"filePath": "C:\\Users\\gbx\\Desktop\\20886216051739200156_20230522.csv.zip"
}
因为解读的是csv文件,这里使用EasyExcel作为工具:
<!-- 解读excel文件 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.2.1</version>
</dependency>
设置读取excel必需的监听类(该监听类技术参考来源:Easy Excel读取复杂表格文件_easyexcel读取复杂excel_MMO_的博客-CSDN博客):
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* EasyExcel监听类
*
* @author gbx
*/
@Slf4j
public class EasyExcelListener extends AnalysisEventListener<Object> {
/**
* 创建list集合封装最终的数据
*/
private List<Object> list = new ArrayList<>();
/**
* sheet页索引
*/
private int sheetNo = 0;
/**
* 具体读取方法(每一行执行一次)
*
* @param t 读取的每一行美容
* @param context
*/
@Override
public void invoke(Object t, AnalysisContext context) {
//读取内容
int currentSheetNo = context.readSheetHolder().getSheetNo();
if (currentSheetNo != sheetNo) {
// 如果不根据sheet页索引更新状态重新创建list,list会反复添加前面的sheet页对象值
list = new ArrayList<>();
sheetNo = currentSheetNo;
}
list.add(t);
}
/**
* 开始读取时执行的方法,用以获取表头信息
*
* @param headMap 存放表头信息
* @param context
*/
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
if (ObjectUtils.isNotEmpty(headMap)) {
list.add(headMap);
}
}
/**
* 读取完毕后执行方法
*
* @param analysisContext
*/
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
log.info("___读取已完成...___");
}
/**
* 获取表格内容(简单excel读取可用该方法)
*
* @param obj 需要转化的实体
* @param <T>
* @return
*/
public <T> List<T> getList(Class<T> obj) {
JSONArray jsonArray = new JSONArray(list);
return JSONUtil.toList(jsonArray, obj);
}
/**
* 将表格转化为map集合(复杂excel读取用此方法)
*
* @return map集合
*/
public List<LinkedHashMap> getListMap() {
JSONArray jsonArray = new JSONArray(list);
return JSONUtil.toList(jsonArray, LinkedHashMap.class);
}
}
然后是读取的工具类:
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.read.builder.ExcelReaderBuilder;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.fastjson2.JSONObject;
import com.gbx.pay.service.monolith.common.utils.file.ZipFileReadUtils;
import lombok.extern.slf4j.Slf4j;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* excel文件读取工具类
*
* @author gbx
*/
@Slf4j
public class EasyExcelReadUtil {
/**
* 存储方法(此处只进行打印模拟)
*
* @param sheetInfos 数据集合
*/
private void saveExcelSheetInfos(Map<Integer, List<LinkedHashMap>> sheetInfos) {
if (sheetInfos == null || sheetInfos.size() <= 0) {
throw new RuntimeException("excel文件内容为空");
}
for (Integer sheetNum : sheetInfos.keySet()) {
log.info("第" + (sheetNum + 1) + "页内容=======================》START");
List<LinkedHashMap> contentMapList = sheetInfos.get(sheetNum);
if (contentMapList != null && contentMapList.size() > 0) {
for (int i = 0; i < contentMapList.size(); i++) {
LinkedHashMap map = contentMapList.get(i);
byte[] bytes = StrUtil.bytes(JSONObject.toJSONString(map), "gbk");
String str = StrUtil.str(bytes, "gbk");
log.info("第" + i + "行内容:" + str);
}
}
log.info("第" + (sheetNum + 1) + "页内容《=======================END");
}
}
/**
* 解读zip文件
*
* @param filePath 文件路径
*/
public void readZip(String filePath) {
try {
List<InputStream> inputStreamList = ZipFileReadUtils.readZipToInputStreamList(filePath, ".csv");
if (inputStreamList.size() > 0) {
for (InputStream inputStream : inputStreamList) {
// step1. 读取excel内容
EasyExcelListener easyExcelListener = new EasyExcelListener();
ExcelReaderBuilder read = EasyExcelFactory.read(inputStream, easyExcelListener).charset(Charset.forName("gbk")).excelType(ExcelTypeEnum.CSV);
ExcelReader excelReader = read.build();
// step2. 获取各个sheet页信息
List<ReadSheet> sheets = excelReader.excelExecutor().sheetList();
// step3. 获取各个Sheet页表格内容存于map
Map<Integer, List<LinkedHashMap>> sheetInfos = new HashMap<>(sheets.size());
for (ReadSheet sheet : sheets) {
Integer sheetNo = sheet.getSheetNo();
excelReader.read(sheet);
sheetInfos.put(sheetNo, easyExcelListener.getListMap());
}
//保存读取出来的数据
saveExcelSheetInfos(sheetInfos);
}
}
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}
此处没有真正执行保存操作,只进行了字符串的展示打印,具体操作按照自己的业务进行下一步操作即可。
展示图:
工具类引用了我前篇博文的一个工具类:
import cn.hutool.core.io.FileUtil;
import com.gbx.pay.service.monolith.common.exception.ui.ErrorException;
import org.apache.commons.lang3.StringUtils;
import java.io.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* zip文件读取工具类
*
* @author gbx
*/
public class ZipFileReadUtils {
/**
* 解读zip文件
*
* @param zipFile 压缩文件
* @param suffixType 文件后缀(非空时只处理固定后缀的文件)
* @return 处理结果
* @throws IOException
*/
public static List<InputStream> readZipToInputStreamList(File zipFile, String suffixType) throws IOException {
List<InputStream> list = new ArrayList<>();
//判断文件是否存在
if (!zipFile.exists()) {
throw new ErrorException("无效的zip文件");
}
//获取文件流
InputStream inputStream = FileUtil.getInputStream(zipFile);
//转化文件流为压缩文件流
ZipInputStream zipInputStream = new ZipInputStream(inputStream, Charset.forName("gbk"));
ZipEntry zipEntry;
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
//如果文件后缀条件不为空且后缀条件不符则跳过文件读取
if (StringUtils.isNotBlank(suffixType) && !zipEntry.getName().endsWith(suffixType)) {
continue;
}
//文件读取处理
byte[] buffer = new byte[1024];
int len;
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
while ((len = zipInputStream.read(buffer)) != -1) {
byteStream.write(buffer, 0, len);
}
// 关闭流
byteStream.close();
//读取的文件转为所需的流添加到集合中
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteStream.toByteArray());
list.add(byteArrayInputStream);
}
return list;
}
/**
* 解读zip文件
*
* @param filePath 压缩文件路径
* @param suffixType 文件后缀(非空时只处理固定后缀的文件)
* @return 处理结果
* @throws IOException
*/
public static List<InputStream> readZipToInputStreamList(String filePath, String suffixType) throws IOException {
File zipFile = new File(filePath);
return readZipToInputStreamList(zipFile, suffixType);
}
}