CSVUtil 实现 csv文件上传下载,判断字符集编码

CSVUtil 实现 csv文件上传下载,判断字符集编码

    • 一、maven依赖
    • 二、CSVUtil.java
    • 三、CSVController.java(测试)
    • 四、测试结果

GitHub: link. 欢迎star

注意:本篇博客风格(不多比比就是撸代码!!!)

一、maven依赖

		
        <dependency>
            <groupId>org.apache.commonsgroupId>
            <artifactId>commons-csvartifactId>
            <version>1.9.0version>
        dependency>

二、CSVUtil.java

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVPrinter;
import org.apache.commons.csv.CSVRecord;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.Function;

/**
 * @author Andon
 * 2021/12/21
 * 

* csv文件上传下载 */ @Slf4j public class CSVUtil { //行尾分隔符定义 private static final String NEW_LINE_SEPARATOR = java.security.AccessController.doPrivileged(new sun.security.action.GetPropertyAction("line.separator")); //上传文件的存储位置 private final static URL PATH = Thread.currentThread().getContextClassLoader().getResource(""); private static final CSVFormat CSV_FORMAT = CSVFormat.DEFAULT.builder().setIgnoreEmptyLines(false).setRecordSeparator(NEW_LINE_SEPARATOR).setQuote(null).build(); /** * 上传文件 * * @param multipartFile MultipartFile */ public static File uploadFile(MultipartFile multipartFile) { assert PATH != null; // 获取上传路径 String path = PATH.getPath() + UUID.randomUUID().toString().replaceAll("-", "") + File.separator + multipartFile.getOriginalFilename(); try { // 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例 File file = new File(path); // 此抽象路径名表示的文件或目录是否存在 if (!file.getParentFile().exists()) { // 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录 boolean mkdirs = file.getParentFile().mkdirs(); } // 转换为一般file文件 multipartFile.transferTo(file); log.info("上传文件成功,文件名===>{}, 路径===>{}", multipartFile.getOriginalFilename(), file.getPath()); return file; } catch (IOException e) { log.error("上传文件失败 error:{} e:{}" + e.getMessage(), e); return null; } } /** * 读取CSV文件的内容 * * @param filePath 文件路径 * @param indexArr 参与计算的列的组合角标 * @return 表内容集合,key是组合ID,value是整行数据 */ public static Map<String, String> readCSVToMap(String filePath, String[] indexArr) throws IOException { String charset = charset(filePath); try (FileInputStream fileInputStream = new FileInputStream(filePath)) { return records(fileInputStream, charset, csvRecords -> { Map<String, String> map = new HashMap<>(); //通过首行获取列数量 int colNum = csvRecords.get(0).size(); for (CSVRecord record : csvRecords) { // 每行的内容 List<String> value = new ArrayList<>(colNum); for (int i = 0; i < colNum; i++) { value.add(record.get(i)); } // 每行ID List<String> key = new ArrayList<>(indexArr.length); for (String index : indexArr) { key.add(record.get(Integer.parseInt(index))); } String id = String.join(",", key); if (!map.containsKey(id)) { map.put(id, String.join(",", value)); } } return map; }); } } /** * 读取CSV文件的内容 * * @param filePath 文件路径 * @param indexArr 参与计算的列的组合角标 * @return 表内容集合,value是参与计算的列的数据 */ public static List<String> readCSVToList(String filePath, String[] indexArr) throws IOException { String charset = charset(filePath); try (FileInputStream fileInputStream = new FileInputStream(filePath)) { return records(fileInputStream, charset, csvRecords -> { List<String> values = new ArrayList<>(); for (CSVRecord record : csvRecords) { // 每行的内容 List<String> value = new ArrayList<>(); if (ObjectUtils.isEmpty(indexArr)) { for (String item : record) { value.add(item.trim()); } } else { value = new ArrayList<>(indexArr.length); for (String index : indexArr) { value.add(record.get(Integer.parseInt(index))); } } values.add(String.join(",", value)); } return values; }); } } /** * 读取CSV数据条目数 * * @param filePath 文件路径 * @return 数据条目数 */ public static long readDataCount(String filePath) { String charset = charset(filePath); try (FileInputStream fileInputStream = new FileInputStream(filePath)) { return records(fileInputStream, charset, csvRecords -> Long.valueOf(csvRecords.size())); } catch (IOException e) { log.error("解析CSV内容失败 error:{} e:{}", e.getMessage(), e); } return 0; } /** * 创建CSV文件 * * @param fileName File * @param head 表头 * @param values 表体 */ public static File makeTempCSV(String fileName, String[] head, List<String[]> values) throws IOException { // 创建文件 assert PATH != null; File file = File.createTempFile(fileName, ".csv", new File(PATH.getPath())); try (BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) { CSVPrinter printer = new CSVPrinter(bufferedWriter, CSV_FORMAT); // 写入表头 printer.printRecord(Arrays.asList(head)); // 写入内容 for (String[] value : values) { printer.printRecord(Arrays.asList(value)); } printer.close(); } return file; } /** * 下载文件 * * @param response HttpServletResponse * @param file File */ public static boolean downloadFile(HttpServletResponse response, File file, String fileName) { FileInputStream fileInputStream = null; BufferedInputStream bufferedInputStream = null; OutputStream os = null; try { // 设置csv文件下载头信息 response.setContentType("application/octet-stream"); response.addHeader("Content-Disposition", "attachment; filename=" + new String(fileName.getBytes(StandardCharsets.UTF_8), "ISO8859-1") + ".csv"); fileInputStream = new FileInputStream(file); bufferedInputStream = new BufferedInputStream(fileInputStream); os = response.getOutputStream(); //MS产本头部需要插入BOM //如果不写入这几个字节,会导致用Excel打开时,中文显示乱码 os.write(new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF}); byte[] buffer = new byte[1024]; int i = bufferedInputStream.read(buffer); while (i != -1) { os.write(buffer, 0, i); i = bufferedInputStream.read(buffer); } return true; } catch (IOException e) { e.printStackTrace(); } finally { //关闭流 if (os != null) { try { os.flush(); os.close(); } catch (IOException e) { e.printStackTrace(); } } if (bufferedInputStream != null) { try { bufferedInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (fileInputStream != null) { try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } boolean delete = file.delete(); } return false; } private static <R> R records(InputStream inputStream, String charset, Function<List<CSVRecord>, R> recordsHandler) throws IOException { try (InputStreamReader inputStreamReader = new InputStreamReader(inputStream, charset); BufferedReader bufferedReader = new BufferedReader(inputStreamReader);) { CSVParser parser = CSV_FORMAT.parse(bufferedReader); // 读取文件每行内容 List<CSVRecord> records = parser.getRecords(); return recordsHandler.apply(records); } } /** * 判断文件字符编码 */ public static String charset(String path) { String charset = "GBK"; byte[] first3Bytes = new byte[3]; try { boolean checked = false; BufferedInputStream bis = new BufferedInputStream(new FileInputStream(path)); bis.mark(0); int read = bis.read(first3Bytes, 0, 3); if (read == -1) { bis.close(); return charset; // 文件编码为 ANSI } else if (first3Bytes[0] == (byte) 0xFF && first3Bytes[1] == (byte) 0xFE) { charset = "UTF-16LE"; // 文件编码为 Unicode checked = true; } else if (first3Bytes[0] == (byte) 0xFE && first3Bytes[1] == (byte) 0xFF) { charset = "UTF-16BE"; // 文件编码为 Unicode big endian checked = true; } else if (first3Bytes[0] == (byte) 0xEF && first3Bytes[1] == (byte) 0xBB && first3Bytes[2] == (byte) 0xBF) { charset = "UTF-8"; // 文件编码为 UTF-8 checked = true; } bis.reset(); if (!checked) { while ((read = bis.read()) != -1) { if (read >= 0xF0) break; if (0x80 <= read && read <= 0xBF) // 单独出现BF以下的,也算是GBK break; if (0xC0 <= read && read <= 0xDF) { read = bis.read(); if (0x80 > read || read > 0xBF) { break; // 双字节 (0xC0 - 0xDF),(0x80 - 0xBF),也可能在GB编码内 } } else if (0xE0 <= read) { // 也有可能出错,但是几率较小 read = bis.read(); if (0x80 <= read && read <= 0xBF) { read = bis.read(); if (0x80 <= read && read <= 0xBF) { charset = "UTF-8"; } } break; } } } bis.close(); } catch (Exception e) { e.printStackTrace(); } log.info("--文件-> [{}] 采用的字符集为: [{}]", path, charset); return charset; } }

三、CSVController.java(测试)

import com.andon.springbootutil.util.CSVUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Andon
 * 2021/12/21
 */
@Slf4j
@Api(tags = "CSV")
@RequestMapping("/csv")
@RestController
public class CSVController {

    @ApiOperation("上传")
    @PostMapping(value = "/upload")
    public List<String> upload(MultipartFile multipartFile) throws IOException {
        File file = CSVUtil.uploadFile(multipartFile);
        assert file != null;
        long count = CSVUtil.readDataCount(file.getPath());
        log.info("文件-> [{}] count={}", file.getPath(), count);
        List<String> list = CSVUtil.readCSVToList(file.getPath(), null);
        boolean delete = file.delete();
        boolean parentFileDelete = file.getParentFile().delete();
        return list;
    }

    @ApiOperation("下载")
    @GetMapping(value = "/download")
    public void download(String fileName, String head, String values, HttpServletResponse httpServletResponse) throws IOException {
        String[] headArr = head.split(",");
        List<String[]> valueList = new ArrayList<>(headArr.length + 1);
        String[] valueArr = values.split("\\|");
        for (String value : valueArr) {
            valueList.add(value.split(","));
        }
        fileName += "_" + System.currentTimeMillis();
        File file = CSVUtil.makeTempCSV(fileName, headArr, valueList);
        boolean b = CSVUtil.downloadFile(httpServletResponse, file, fileName);
    }
}

四、测试结果

CSVUtil 实现 csv文件上传下载,判断字符集编码_第1张图片
CSVUtil 实现 csv文件上传下载,判断字符集编码_第2张图片
CSVUtil 实现 csv文件上传下载,判断字符集编码_第3张图片
GitHub: link. 欢迎star

你可能感兴趣的:(工具类,CSV,java,csv,后端)