當時導入的時候用的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的生成,如果有更好的方法,歡迎評論區留言。