Java工具类TxtToExcel

@Ta 2022-09-03发布,2022-09-07修改 1649点击
如题,顾名思义!在此感谢@yiluo@消失的安装包,的热心帮助
to.jpg(86.57 KB)

功能描述:把.txt文件解析成.xls文件;
参数说明:传参( 待解析的文件.txt,缓存文件夹的绝对路径,原始数据的分隔符,不传最后一个字段就是解析所有列 or 反之则根据参数解析指定列 )
使用示例:(补充:在代码中放了一个示例Demo)
String excelFileName = TxtToExcelUtil.txtToExcel(file, cacheFolder, delimiter); // 解析所有字段
String excelFileName = TxtToExcelUtil.txtToExcel(file, cacheFolder, delimiter, selectField); // 解析指定字段



package com.tabkey9.common.utils;

import cn.hutool.poi.excel.ExcelWriter;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;


/**
 * txt 转 Excel
 *
 * @author TabKey9
 * @version v2.1
 */
public class TxtToExcelUtil {

    /**
     * 给保存的临时文件加前缀,后续根据前缀删除文件;如:"TxtToExcel_*******.txt" and "TxtToExcel_*******.xls"
     */
    private static String PREFIX;
    /**
     * 用于下载的 Excel文件名
     */
    private static String EXCELFILENAME;
    /**
     * 缓存文件夹的绝对路径,如:"D:/cacheFolder/"
     */
    private static String CACHEFOLDER;
    /**
     * 原始数据的分隔符,如: "\\|"
     */
    private static String DELIMITER;
    /**
     * 文件名命名规则,如:自定义字符串 + 时间戳
     */
    private static String FILENAME;
    /**
     * 输出到 Excel的字段,规则: 不传、为null则输出所有字段,如:map.put("1","品牌名称"); 注意:索引从0开始
     */
    private static Map<String, String> SELECTFIELD;
    /**
     * 标题行的索引,默认是第一行(索引0),大概率不需要修改
     */
    private static Integer INDEXOFHEADERROW;

    // 示例:写个Demo
/*    public R parseTxtFile(MultipartFile file){
        String delimiter = "\\|";
        HashMap<String, String> selectField = new HashMap<>();
        selectField.put("1","品牌名称");
        try {
//            String excelFileName = TxtToExcelUtil.txtToExcel(file, cacheFolder, delimiter); // 全字段
            String excelFileName = TxtToExcelUtil.txtToExcel(file, cacheFolder, delimiter, selectField); // 指定字段
            return R.ok().setData(excelFileName);
        } catch (Exception e) {
            e.printStackTrace();
            return R.error("解析失败");
        }
    }*/


    /**
     * Txt To Excel
     * Util - 第一个入口 - 全选字段
     *
     * @param file        MultipartFile
     * @param cacheFolder 缓存文件夹绝对路径
     * @return String EXCELFILENAME 用于下载的 Excel文件名
     * @throws Exception
     */
    public static String txtToExcel(MultipartFile file, String cacheFolder, String delimiter) throws Exception {
        PREFIX = "TxtToExcel";
        DELIMITER = delimiter;
        CACHEFOLDER = cacheFolder;
        FILENAME = PREFIX + "_" + new Date().getTime();
        EXCELFILENAME = FILENAME + "_result.xls";

        earmarkPrefixDelFile(); // 先清缓存

        String s = multipartFileToFile(file);
        if (StringUtils.isNotBlank(s)) {
            parseTxtFile(s);
        }
        return EXCELFILENAME;
    }


    /**
     * Txt To Excel
     * Util - 第二个入口 - 指定字段
     *
     * @param file        MultipartFile
     * @param cacheFolder 缓存文件夹绝对路径
     * @return String EXCELFILENAME 用于下载的 Excel文件名
     * @throws Exception
     */
    public static String txtToExcel(MultipartFile file, String cacheFolder, String delimiter, Map<String, String> selectField) throws Exception {
        PREFIX = "TxtToExcel";
        DELIMITER = delimiter;
        CACHEFOLDER = cacheFolder;
        FILENAME = PREFIX + "_" + new Date().getTime();
        EXCELFILENAME = FILENAME + "_result.xls";
        if (selectField.size() > 0) {
            SELECTFIELD = selectField;
            INDEXOFHEADERROW = 0;
        }

        earmarkPrefixDelFile(); // 先清缓存

        String s = multipartFileToFile(file);
        if (StringUtils.isNotBlank(s)) {
            parseTxtFile(s);
        }
        return EXCELFILENAME;
    }


    /**
     * 1、指定文件前缀删除文件
     */
    private static void earmarkPrefixDelFile() {
        if (!StringUtils.isEmpty(CACHEFOLDER)) {
            File dir = new File(CACHEFOLDER);
            File[] files = dir.listFiles();
            for (File f : files) {
                //判断文件后缀
                if (f.getName().startsWith(PREFIX)) {
                    if (f.delete()) {
                        System.out.println("已删除缓存文件 --> " + f.getName());
                    }
                }
            }
        }
    }


    /**
     * 2、MultipartFile  to File,保存到缓存文件夹上,返回文件的绝对路径
     */
    private static String multipartFileToFile(MultipartFile file) throws Exception {
        File toFile = null;
        if (file.equals("") || file.getSize() <= 0) {
            file = null;
        } else {
            InputStream ins = null;
            ins = file.getInputStream();
            String path = CACHEFOLDER + FILENAME + "_" + file.getOriginalFilename();
//            System.out.println(path);
            toFile = new File(path);
            inputStreamToFile(ins, toFile);
            ins.close();
        }
        return toFile.getPath();
    }


    /**
     * 3、inputStream To File
     */
    private static void inputStreamToFile(InputStream ins, File file) {
        try {
            OutputStream os = new FileOutputStream(file);
            int bytesRead = 0;
            byte[] buffer = new byte[8192];
            while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            ins.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 4、、解析 txt 写入 Excel,保存到指定目录下,返回下载链接
     */
    private static void parseTxtFile(String path) throws Exception {
        List<String> stringList = readRequset(path); // jump 5

        List<ArrayList<String>> result = new ArrayList<>(); // 存结果集,后续将它写入Excel

        // 处理从txt文档读取的数据,用分隔符拆分
        for (String s : stringList) {
            ArrayList<String> list = new ArrayList<>();
            String[] split = s.split(DELIMITER); // 分割符,如  "\\|"
            if (split.length >= 2 && null == SELECTFIELD) {
                for (int i = 0; i < split.length; i++) {
                    // 输出所有字段
                    list.add(split[i].trim());
                }
                result.add(list);
            } else if (split.length >= 2 && SELECTFIELD.size() > 0) {
                // 输出指定字段
                for (String key : SELECTFIELD.keySet()) {
                    list.add(split[Integer.parseInt(key)].trim());
                }
                result.add(list);
            }
        }

        // 替换默认的表头:通常第一行是表头,在这里做自定义表头处理(result 里包含所有解析后的数据)
        if (result.size() > 0 && null != SELECTFIELD && SELECTFIELD.size() > 0) {
            ArrayList<String> tempList = new ArrayList<>();
            for (Map.Entry<String, String> entry : SELECTFIELD.entrySet()) {
                tempList.add(entry.getValue());
//                System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
            }
            result.set(INDEXOFHEADERROW, tempList);
        }

        // 保存路径+文件名
        String savaPath = CACHEFOLDER + EXCELFILENAME;
        //通过构造方法创建writer
        ExcelWriter writer = new ExcelWriter(savaPath);
        writer.disableDefaultStyle();// 禁用默认样式
        writer.setStyleSet(null);// 设置样式集,如果不使用样式,传入null
        writer.write(result); // 一次性写出内容
        writer.autoSizeColumnAll(); // 设置所有列为自动宽度,不考虑合并单元格
        writer.close();// 关闭writer,释放内存
    }

    /**
     * 5、、读取Txt文件流
     *
     * @param path 文件路径
     * @return list 结果集
     */
    private static List<String> readRequset(String path) throws Exception {
        ArrayList<String> list = new ArrayList<>();
        File file = new File(path);
        if (!file.exists()) {
            return list;
        }
        try {
            if (file.isDirectory()) {
                readRequset(file.getAbsolutePath()); // jump 5
            } else {
                String filename = file.getName();
                String ext = filename.substring(filename.lastIndexOf(".") + 1); //截取点后面的字符串   lastIndexOf返回最后一次出现的位置
                if ("TXT".equalsIgnoreCase(ext)) {
                    FileInputStream fileInputStream = new FileInputStream(file);
                    String charset = charset(path); // jump 6 获取文件编码
                    InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, charset);
                    BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
                    // bufferedReader.readLine();// this will read the first line
                    String line = "";
                    while ((line = bufferedReader.readLine()) != null) {
                        list.add(line.trim());
                    }
                    bufferedReader.close();
                    inputStreamReader.close();
                    fileInputStream.close();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return list;
    }

    /**
     * 6、判断文本文件的字符集,文件开头三个字节表明编码格式。
     *
     * @param path,
     * @return charset,
     * 举例:
     * C:/Users/EDY/Desktop/Brand1.txt;
     * <p>
     * --文件-> [C:/Users/EDY/Desktop/Brand1.txt] 采用的字符集为: [GBK]
     */
    private 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); // 读者注: bis.mark(0);修改为 bis.mark(100);我用过这段代码,需要修改上面标出的地方。
            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) // 双字节 (0xC0 - 0xDF)
                            // (0x80 - 0xBF),也可能在GB编码内
                            continue;
                        else
                            break;
                    } else if (0xE0 <= read && read <= 0xEF) { // 也有可能出错,但是几率较小
                        read = bis.read();
                        if (0x80 <= read && read <= 0xBF) {
                            read = bis.read();
                            if (0x80 <= read && read <= 0xBF) {
                                charset = "UTF-8";
                                break;
                            } else
                                break;
                        } else
                            break;
                    }
                }
            }
            bis.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
//        System.out.println("--文件-> [" + path + "] 采用的字符集为: [" + charset + "]");
        return charset;
    }


}


回复列表(5|隐藏机器人聊天)
  • @Ta / 2022-09-03 / /
    有老婆的男人就是有动力
  • @Ta / 2022-09-03 / /

    看起来好复杂呀。。不考虑csv

    还能流式传输,不用写文件

  • @Ta / 2022-09-04 / /
    @无名啊,当时我也犹豫了一下,但不考虑,因为是写给客户自己操作导出的文件,客户可能不知道csv,但一定知道Excel
  • @Ta / 2022-09-04 / /
    另外,导出的数据是为了在另一个接口导入数据库中!当然是做到格式统一比较好啦
  • @Ta / 2022-09-05 / /
    更新了代码,今天改动挺多,暂时就这样,已经满足我个人需求了
添加新回复
回复需要登录