如题,顾名思义!在此感谢
@yiluo,
@消失的安装包,的热心帮助

功能描述:把.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;
}
}
看起来好复杂呀。。不考虑
csv
嘛还能流式传输,不用写文件