**前言**:大佬说要让做zip文件的导入导出,要通用,并且因为是在容器中运行,所以不能通过new File创建文件的方式去创建csv文件和zip文件.
**申明**:代码中有部分通过反射获取属性的部分可以忽略,实际情况看导出列是否固定,代码中因为考虑到数据库中的字段可能有变化,所以没有写死。 代码没有额外引入依赖。
第一步:创建文件导入导出对象
import java.util.List;
/**
* @author zzq
* @Date 2021-06-26 18:29
* 文件导出对象
*/
public class ExportObject {
/**
* 导出第一行,即header csv文件的第一行
*/
private List<String> head;
/**
* 数据,需要与 head长度一致 csv文件的每一行数据
*/
private List<Object> dataList;
/**
* 文件名
*/
private String fileName;
/**
* 文件后缀
*/
private String fileSuf;
/**
* 属性字段名 对象中的属性,通过反射获取
*/
private List<String> fields;
public List<String> getFields() {
return fields;
}
public void setFields(List<String> fields) {
this.fields = fields;
}
public List<String> getHead() {
return head;
}
public void setHead(List<String> head) {
this.head = head;
}
public List<Object> getDataList() {
return dataList;
}
public void setDataList(List<Object> dataList) {
this.dataList = dataList;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getFileSuf() {
return fileSuf;
}
public void setFileSuf(String fileSuf) {
this.fileSuf = fileSuf;
}
}
第二步:导出对象组装
/**
* 组装导出参数
* @param dataList 需要导出的数据
* @param obj 数据类型,用于获取表头,文件名
* @param includeCol 导出需要的列
* @return
*/
public static ExportObject buildExportObject(List<Object> dataList,Class obj,List<String> includeCol){
//表名从类注解获取
Annotation annotation = obj.getAnnotation(Table.class);
String name;
if(annotation==null){
name = obj.getTypeName();
}else {
Table table = (Table) annotation;
name = table.name();
}
return buildExportObject(dataList,obj,includeCol,name,".csv");
}
/**
* 组装参数
* @param dataList 数据列
* @param obj 数据类属性
* @param includeCol 导出列
* @param fileName 导出文件名
* @param fileSuf 导出文件后缀
* @return
*/
public static ExportObject buildExportObject(List<Object> dataList,Class obj,List<String> includeCol,String fileName,String fileSuf){
ExportObject exportObject = new ExportObject();
exportObject.setFileName(fileName);
exportObject.setFileSuf(fileSuf);
exportObject.setDataList(dataList);
List<String> head = new ArrayList<>();
Field[] fields = obj.getDeclaredFields();
try {
for (Field field : fields) {
Column annotation = field.getAnnotation(Column.class);
if(annotation==null){
continue;
}
String colName = annotation.name();
if(!includeCol.contains(field.getName())){
continue;
}
head.add(colName);
}
exportObject.setHead(head);
exportObject.setFields(includeCol);
return exportObject;
}catch (Exception e){
log.error("buildExportObject_error,fileName={} exception={}",fileName,GsonUtil.toJsonStr(e));
}
return null;
}
第三步:到了这一步,导出所需要的文件名,文件格式,还有需要导出的数据,导出列 都已经确定了,接下来就是通过流将数据保存
private Object exportData(List<ExportObject> exportObjects, String exportTaskName){
if(CollectionUtils.isEmpty(exportObjects)){
return ResponseEntity.of(Optional.of ("export data empty"));
}
ByteArrayOutputStream byteOutputStream = null;
ZipOutputStream zipOutputStream = null;
byte[] bytes;
try {
byteOutputStream = new ByteArrayOutputStream();
zipOutputStream = new ZipOutputStream(byteOutputStream, StandardCharsets.UTF_8);
for (ExportObject exportObject : exportObjects) {
String export = FileUtils.export(exportObject);
//生成文件名等信息
ZipEntry zipEntry = new ZipEntry(exportObject.getFileName() + exportObject.getFileSuf());
zipOutputStream.putNextEntry(zipEntry);
//压缩到outputStream
zipOutputStream.write(new byte[]{(byte)0xEF, (byte)0xBB, (byte)0xBF});
zipOutputStream.write(export.getBytes(StandardCharsets.UTF_8));
zipOutputStream.closeEntry();
}
zipOutputStream.close();
byteOutputStream.close();
bytes = byteOutputStream.toByteArray();
} catch (IOException e) {
e.printStackTrace();
log.warn(e,"zip压缩异常");
throw new HsjryException("500","zip压缩异常");
}
//输出
DateFormat format = new SimpleDateFormat("yyyy-MM-dd_HH_mm");
String format1 = format.format(new Date());
String fieldName = "导出数据_"+format1+".zip";
try {
InputStream is = new ByteArrayInputStream(bytes);
byteOutputStream.close();
HttpHeaders headers = new HttpHeaders();
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
headers.add("Content-Disposition", String.format("attachment; filename=\"%s\" ", URLEncoder.encode(fieldName,"UTF-8")));
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
return ResponseEntity
.ok()
.headers(headers)
//.contentType(MediaType.parseMediaType("application/zip;charset=UTF-8"))
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(new InputStreamResource(is));
}catch (Exception e){
log.error("data_export_error ,taskName:{},exportRequest:{}");
}
return ResponseEntity.of(Optional.of ("export error"));
}
/**
* 连接符
*/
private static final String CONNECT_CHARACTER = ",";
private static final String LINE_CHARACTER = "\r\n";
//此方法中的数据加解密可以不需要
public static String export(ExportObject exportObject) {
try {
StringBuilder builder = new StringBuilder();
StringBuilder fileHead = new StringBuilder();
for (String col : exportObject.getHead()) {
fileHead.append(col).append(CONNECT_CHARACTER);
}
String headEncrypt = encryptToString(fileHead.toString());
builder.append(headEncrypt);
builder.append(LINE_CHARACTER);
for (Object row : exportObject.getDataList()) {
StringBuilder line = new StringBuilder();
Map<String, Object> stringMap = GsonUtil.toObjectMap(row);
for (String field : exportObject.getFields()) {
Object value = stringMap.get(field);
//TENANT_ID,OPERATOR_ID 可以不需要,是因数字太长可能被转成科学计数法的属性,做的额外处理
if(Objects.equals(TENANT_ID,field) || Objects.equals(OPERATOR_ID,field)){
line.append("\t" +value+"\t").append(CONNECT_CHARACTER);
}else{
if(value!=null){
String str = value.toString();
//先判断字符里是否含有逗号
if(value instanceof String){
if(str.contains(",")){
//如果还有双引号,先将双引号转义,避免两边加了双引号后转义错误
if(str.contains("\"")){
str=str.replace("\"", "\"\"");
}
//将逗号转义
str="\""+str+"\"";
line.append(str).append(CONNECT_CHARACTER);
}else if(isNumeric(str.trim())){
//判断是否为纯数字,如果是的话可能会因为太长而现实显示为科学记数法
line.append("\t" +value+"\t").append(CONNECT_CHARACTER);
}else{
//其他格式字符串,正常写入
line.append(value).append(CONNECT_CHARACTER);
}
}else {
line.append(value).append(CONNECT_CHARACTER);
}
}else {
line.append(value).append(CONNECT_CHARACTER);
}
}
}
//对数据加密
String encryptString = encryptToString(line.toString());
builder.append(encryptString);
builder.append(LINE_CHARACTER);
}
return builder.toString();
}catch (Exception e){
log.error(e,"export#error");
throw new HsjryException("500","生成csv内容异常");
}
}
第四步:示例
List<ExportObject> exportObjectList = new ArrayList<>();
//1. expandVarList 为从数据库查询出来的数据
ExportObject expandVarObject = FileUtils.buildExportObject(new ArrayList<>(list4), xxx.class, Arrays.asList(FileUtils.getExportFiled(xxx.class)));
exportObjectList.add(expandVarObject);
/**
* 所有导出排除的列
*/
private static final List<String> EXCLUDE_FIELD = new ArrayList<String>(){{
add("serialVersionUID");
}};
public static String[] getExportFiled(Class clazz){
String[] strings = new ArrayList<String>() {{
List<String> collect = Arrays.stream(clazz.getDeclaredFields()).map(field -> {
//排除所有带有@Id 的字段
Id annotation = field.getAnnotation(Id.class);
if (annotation == null) {
return field;
}
return null;
}).filter(Objects::nonNull).map(Field::getName).filter(name -> !EXCLUDE_FIELD.contains(name)).collect(Collectors.toList());
addAll(collect);
}}.toArray(FIELD_ARRAY);
return strings;
}