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的生成,如果有更好的方法,欢迎评论区留言。

猜你喜欢

转载自www.cnblogs.com/ailanlan/p/12172952.html