Java之利用openCsv導出csv文件


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM