当时导入的时候用的openCsv,那么导出的时候自然也是用这个,查了好多资料才找到解决方案,下面记录一下实现过程。
1.Controller层:
/** * 导出csv文件 */ @RequestMapping("/exportcsv") @RequiresPermissions("xxx:xxxx:xxx") public String exportCsv(@RequestBody List<xxxEntity> exportResults, HttpServletResponse response) { return xxxService.exportCsvFile(exportResults, response); }
2.实现类部分:
@Override public String exportCsvFile(List<xxxEntity> exportResults, HttpServletResponse response) { try { CSVUtils<xxxEntity> xxx = new CSVUtils(); xxx.generateCsvFile(exportResults, "exportResults.csv", HEADER); xxx.readCsvFileStream("exportResults.csv", response); } catch (IOException | CsvDataTypeMismatchException | CsvRequiredFieldEmptyException e) { log.error("EXPORT ERROR", e); } return null; }
3.核心Util导出方法:
import com.opencsv.CSVWriter; import com.opencsv.bean.StatefulBeanToCsv; import com.opencsv.bean.StatefulBeanToCsvBuilder; import com.opencsv.exceptions.CsvDataTypeMismatchException; import com.opencsv.exceptions.CsvRequiredFieldEmptyException; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.util.List; /** * CSV 工具类 * * @author jing */ @Slf4j @Component public class CSVUtils<T>{ /** * 将前台传递的数据生成csv文件 * @param exportResults * @param fileName * @param header * @throws IOException * @throws CsvDataTypeMismatchException * @throws CsvRequiredFieldEmptyException */ public static<T> void generateCsvFile(List<T> exportResults, String fileName, String[] header) throws IOException, CsvDataTypeMismatchException, CsvRequiredFieldEmptyException { Writer writer = new FileWriter(fileName); // 写表头 CSVWriter csvWriter = new CSVWriter(writer, CSVWriter.DEFAULT_SEPARATOR, CSVWriter.NO_QUOTE_CHARACTER, CSVWriter.NO_ESCAPE_CHARACTER, CSVWriter.DEFAULT_LINE_END); csvWriter.writeNext(header); //写内容 StatefulBeanToCsv beanToCsv = new StatefulBeanToCsvBuilder(writer).build(); beanToCsv.write(exportResults); csvWriter.close(); writer.close(); } /** * 读取csv文件流返回前端下载 * @param fileName * @param response * @throws UnsupportedEncodingException */ public static void readCsvFileStream(String fileName, HttpServletResponse response) throws UnsupportedEncodingException { String myFileName = new String(fileName.getBytes("utf-8"), "gbk"); File file = new File(myFileName); if (file.exists()) { response.setContentType("application/force-download");// 设置强制下载不打开 response.addHeader("Content-Disposition", "attachment;fileName=" + myFileName);// 设置文件名 byte[] buffer = new byte[1024]; FileInputStream fis = null; BufferedInputStream bis = null; try { fis = new FileInputStream(file); bis = new BufferedInputStream(fis); OutputStream os = response.getOutputStream(); int i = bis.read(buffer); while (i != -1) { os.write(buffer, 0, i); i = bis.read(buffer); } } catch (Exception e) { e.printStackTrace(); } finally { if (bis != null) { try { bis.close(); } catch (IOException e) { e.printStackTrace(); } } if (fis != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } } if(file.delete()){ log.error(file.getName() + " 文件已被删除!"); }else{ log.error("文件删除失败!"); } } }
这里的思路是后端接收前端发来的list,然后利用openCsv写入生成csv文件,再从csv文件中读取文件流返回前端下载。
下面是项目中的两个问题:
1.如果不指定csv文件中的顺序,那么他是基于列名升序排列,那么这里就需要用@CsvBindByPosition(position = 0)来定位位置,但是如果你用这个来定位的话,那么表头就展示不出来,如果@CsvBindByName的话,又定位不了位置,那么这里我的解决方案就是,用@CsvBindByPosition(position = 0)来定位位置,表头的话再自己写入。
2.如果列中出现了时间相关的数据,那么他展示的数据是GMT+8这种格式,这时候的解决方案是用@CsvDate("yyyy-MM-dd HH:mm:ss")来进行时间格式化。
我的实体类大概长这样儿:
/** * 用户 */ @Data @TableName("user") public class UserEntity implements Serializable { private static final long serialVersionUID = 1L; @TableId @CsvBindByPosition(position = 0) private Long id; /** * 用户名 */ @NotBlank(message = "用户名不能为空") @CsvBindByPosition(position = 1) private String userName; /** * 创建时间 */ @CsvBindByPosition(position = 2) @CsvDate("yyyy-MM-dd HH:mm:ss") @JSONField(format = "yyyy-MM-dd HH:mm:ss") @TableField(fill = FieldFill.INSERT) private Date createTime; /** * 修改时间 */ @CsvBindByPosition(position = 3) @CsvDate("yyyy-MM-dd HH:mm:ss") @JSONField(format = "yyyy-MM-dd HH:mm:ss") @TableField(fill = FieldFill.UPDATE) private Date updateTime; }
这中导出方式需要有一个中间文件csv的生成,如果有更好的方法,欢迎评论区留言。