2022-12-09 今天整理一下最近的excel导入导出经验,整理出了几个工具类,放心使用,可应付8成需求
excel导入导出现实需求可能很复杂,但基本都可以通过原生和easyexcel配合实现,开发需要灵活些。
下述工具类会不断完善,欢迎提出意见,由于为实现复杂需求,所以代码从excel拿数据拿的比较细致,未作代码优化。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.0.5</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>3.17</version>
</dependency>
一、文件操作工具类
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.http.entity.ContentType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
/**
* @Author: NanMing
* @Date: 2022/12//11:02
* @Description MultipartFile转File、inputStream 转 File、file 转 MultipartFile
*
* <dependency>
* <groupId>commons-fileupload</groupId>
* <artifactId>commons-fileupload</artifactId>
* <version>1.3.2</version>
* </dependency>
* <dependency>
* <groupId>org.springframework</groupId>
* <artifactId>spring-test</artifactId>
* <version>5.0.8.RELEASE</version>
* </dependency>
* <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
* <dependency>
* <groupId>org.apache.httpcomponents</groupId>
* <artifactId>httpclient</artifactId>
* <version>4.5.10</version>
* </dependency>
*
* <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpcore -->
* <dependency>
* <groupId>org.apache.httpcomponents</groupId>
* <artifactId>httpcore</artifactId>
* <version>4.4.12</version>
* </dependency>
*/
public class FileToolUtil extends FileUtil {
private static final Logger logger = LoggerFactory.getLogger(FileToolUtil.class);
/**
* MultipartFile转File
*/
public static File toFile(MultipartFile multipartFile)
{
// 获取文件名
String fileName = multipartFile.getOriginalFilename();
// 获取文件后缀
String prefix = "." + getExtensionName(fileName);
File file = null;
try
{
// 用uuid作为文件名,防止生成的临时文件重复
file = new File(FileUtils.getTempDirectory() + File.separator + IdUtil.simpleUUID() + prefix);
// MultipartFile to File
multipartFile.transferTo(file);
}
catch (IOException e)
{
logger.error(e.getMessage(), e);
}
return file;
}
/**
* 获取文件扩展名,不带 .
*/
public static String getExtensionName(String filename)
{
if ((filename != null) && (filename.length() > 0))
{
int dot = filename.lastIndexOf('.');
if ((dot > -1) && (dot < (filename.length() - 1)))
{
return filename.substring(dot + 1);
}
}
return filename;
}
/**
* Java文件操作 获取不带扩展名的文件名
*/
public static String getFileNameNoEx(String filename)
{
if ((filename != null) && (filename.length() > 0))
{
int dot = filename.lastIndexOf('.');
if ((dot > -1) && (dot < (filename.length())))
{
return filename.substring(0, dot);
}
}
return filename;
}
/**
* inputStream 转 File
*/
static File inputStreamToFile(InputStream ins, String name)
{
File file = new File(FileUtils.getTempDirectory() + File.separator + name);
if (file.exists())
{
return file;
}
OutputStream os = null;
try
{
os = new FileOutputStream(file);
int bytesRead;
int len = 8192;
byte[] buffer = new byte[len];
while ((bytesRead = ins.read(buffer, 0, len)) != -1)
{
os.write(buffer, 0, bytesRead);
}
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
IOUtils.closeQuietly(os);
IOUtils.closeQuietly(ins);
}
return file;
}
/**
* @Description: file 转 MultipartFile
* @param file
* @return: MultipartFile
*/
public static MultipartFile fileToMultipartFile(File file)
{
FileInputStream fileInputStream = null;
MultipartFile multipartFile = null;
try
{
fileInputStream = new FileInputStream(file);
multipartFile = new MockMultipartFile(file.getName(), file.getName(), ContentType.APPLICATION_OCTET_STREAM.toString(), fileInputStream);
}
catch (Exception e)
{
e.printStackTrace();
}
return multipartFile;
}
/**
* 递归获取指定文件夹下所有文件,不含文件夹
* @param dirFilePath 文件夹路径
* @param rejectFileNames 过滤掉的文件
* @return List<File>
*/
public static List<File> getAllFile(String dirFilePath, List<String> rejectFileNames)
{
if (StringUtils.isEmpty(dirFilePath))
{
return null;
}
return getAllFile(new File(dirFilePath), rejectFileNames);
}
/**
* 递归获取指定文件夹下所有文件
* @param dirFile 文件
* @param rejectFileNames 过滤掉的文件
* @return List<File>
*/
public static List<File> getAllFile(File dirFile, List<String> rejectFileNames)
{
// 如果文件夹不存在或着不是文件夹,则返回 null
if (Objects.isNull(dirFile) || !dirFile.exists() || dirFile.isFile())
{
return null;
}
File[] childrenFiles = dirFile.listFiles();
if (Objects.isNull(childrenFiles) || childrenFiles.length == 0)
{
return null;
}
List<File> files = new ArrayList<>();
for (File childFile : childrenFiles)
{
// 如果是文件,直接添加到结果集合
if (childFile.isFile() && !rejectFileNames.contains(childFile.getName()))
{
files.add(childFile);
}
else
{
// 如果是文件夹,则将其内部文件添加进结果集合
List<File> cFiles = getAllFile(childFile, rejectFileNames);
if (Objects.isNull(cFiles) || cFiles.isEmpty()) {continue;}
files.addAll(cFiles);
}
}
return files;
}
/**
* 将压缩文件中指定后缀名称的文件解压到指定目录
* @param compressFile 压缩文件
* @param baseDirectory 解压到的基础目录(在此目录下创建UUID的目录,存入解压的文件)
* @param decompressSuffs 要提取文件的后缀名
* @return
*/
public static void decompressToDirectory(File compressFile, String baseDirectory, List<String> decompressSuffs) throws Exception
{
InputStream in = null;
FileOutputStream out = null;
ZipFile zip = null;
try
{
// 验证压缩文件
boolean isFile = compressFile.isFile();
if (!isFile)
{
logger.error(String.format("compressFile非文件格式!", compressFile.getName()));
return;
}
String compressFileSuff = FileUtil.getSuffix(compressFile.getName()).toLowerCase();
if (!compressFileSuff.equals("zip"))
{
logger.error(String.format("[%s]文件非zip类型的压缩文件!", compressFile.getName()));
return;
}
// 解压缩文件(zip)
zip = new ZipFile(new File(compressFile.getAbsolutePath()), Charset.forName("GBK"));// 解决中文文件夹乱码
for (Enumeration<? extends ZipEntry> entries = zip.entries(); entries.hasMoreElements();)
{
ZipEntry entry = entries.nextElement();
String zipEntryName = entry.getName();
// 过滤非指定后缀文件
String suff = FileUtil.getSuffix(zipEntryName).toLowerCase();
if (decompressSuffs != null && decompressSuffs.size() > 0)
{
if (decompressSuffs.stream().filter(s -> s.equals(suff)).collect(Collectors.toList()).size() <= 0)
{
continue;
}
}
// 创建解压目录(如果复制的代码,这里会报错,没有StrUtil,这里就是创建了一个目录来存储提取的文件,你可以换其他方式来创建目录)
File group = new File(baseDirectory);
if (!group.exists())
{
group.mkdirs();
}
// 解压文件到目录
String outPath = (baseDirectory + File.separator + zipEntryName).replaceAll("\\*", "/");
in = zip.getInputStream(entry);
out = new FileOutputStream(outPath);
byte[] buf1 = new byte[1024];
int len;
while ((len = in.read(buf1)) > 0)
{
out.write(buf1, 0, len);
}
in.close();
out.close();
}
}
catch (Exception e)
{
logger.error(e.getMessage());
}finally {
if(in != null) {
IOUtils.closeQuietly(in);
}
if(out != null) {
IOUtils.closeQuietly(out);
}
if(zip != null) {
IOUtils.closeQuietly(zip);
}
}
// 删除临时zip文件
logger.info("开始删除{}临时文件过程====>>>",compressFile.getAbsolutePath());
del(compressFile.getAbsolutePath());
logger.info("结束删除{}临时文件过程====>>>",compressFile.getAbsolutePath());
}
}
二、文件夹工具类
import cn.hutool.core.util.StrUtil;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* @Author: NanMing
* @Date: 2022/12//10:20
* @Description 文件夹工具类
*/
public class DirFileUtils {
/**
* 获取指定文件夹下所有文件,不含文件夹里的文件
*
* @param dirFilePath 文件夹路径
* @return
*/
public static List<File> getAllFile(String dirFilePath) {
if (StrUtil.isBlank(dirFilePath)) {return null;}
return getAllFile(new File(dirFilePath));
}
/**
* 获取指定文件夹下所有文件,不含文件夹里的文件
*
* @param dirFile 文件夹
* @return
*/
public static List<File> getAllFile(File dirFile) {
// 如果文件夹不存在或着不是文件夹,则返回 null
if (Objects.isNull(dirFile) || !dirFile.exists() || dirFile.isFile()) {return null;}
File[] childrenFiles = dirFile.listFiles();
if (Objects.isNull(childrenFiles) || childrenFiles.length == 0) {return null;}
List<File> files = new ArrayList<>();
for (File childFile : childrenFiles) {
// 如果是文件,直接添加到结果集合
if (childFile.isFile()) {
files.add(childFile);
}
//以下几行代码取消注释后可以将所有子文件夹里的文件也获取到列表里
// else {
// // 如果是文件夹,则将其内部文件添加进结果集合
// List<File> cFiles = getAllFile(childFile);
// if (Objects.isNull(cFiles) || cFiles.isEmpty()) continue;
// files.addAll(cFiles);
// }
}
return files;
}
/**
* 创建文件夹
* @param path 路径
*/
public static void createFile(String path) {
File file = new File(path);
//判断文件是否存在;
if (!file.exists()) {
//创建文件;
file.mkdirs();
}
}
/**
* 删除文件夹及文件夹下所有文件
*
* @param dir 文件地址
* @return boolean
*/
public static boolean deleteDir(File dir) {
if (dir == null || !dir.exists()) {
return true;
}
if (dir.isDirectory()) {
String[] children = dir.list();
//递归删除目录中的子目录下
for (String child : children) {
boolean success = deleteDir(new File(dir, child));
if (!success) {
return false;
}
}
}
// 目录此时为空,可以删除
return dir.delete();
}
/**
* @param path 要压缩的文件路径
* @param format 生成的格式(zip、rar)
* @param zipPath zip的路径
* @param zipName zip文件名
* @Description 将多个文件进行压缩到指定位置
*/
public static boolean generateFile(String path, String format, String zipPath, String zipName) throws Exception {
File file = new File(path);
// 压缩文件的路径不存在
if (!file.exists()) {
throw new Exception("路径 " + path + " 不存在文件,无法进行压缩...");
}
// 用于存放压缩文件的文件夹
String generateFile = zipPath + File.separator;
File compress = new File(generateFile);
// 如果文件夹不存在,进行创建
if (!compress.exists()) {
compress.mkdirs();
}
// 目的压缩文件
String generateFileName = compress.getAbsolutePath() + File.separator + zipName + "." + format;
// 输出流
FileOutputStream outputStream = new FileOutputStream(generateFileName);
// 压缩输出流
ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(outputStream));
//压缩
generateFile(zipOutputStream, file, "");
System.out.println("源文件位置:" + file.getAbsolutePath() + ",目的压缩文件生成位置:" + generateFileName);
// 关闭 输出流
zipOutputStream.close();
return true;
}
/**
* 生成压缩包
* @param srcfile
* @param zipfile
*/
public static void ZipFiles(File[] srcfile, File zipfile) {
byte[] buf = new byte[1024];
try {
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(
zipfile));
for (int i = 0; i < srcfile.length; i++) {
FileInputStream in = new FileInputStream(srcfile[i]);
out.putNextEntry(new ZipEntry(srcfile[i].getName()));
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
out.closeEntry();
in.close();
}
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* @param out 输出流
* @param file 目标文件
* @param dir 文件夹
* @throws Exception
*/
private static void generateFile(ZipOutputStream out, File file, String dir) throws Exception {
// 当前的是文件夹,则进行一步处理
if (file.isDirectory()) {
//得到文件列表信息
File[] files = file.listFiles();
//将文件夹添加到下一级打包目录
out.putNextEntry(new ZipEntry(dir + "/"));
dir = dir.length() == 0 ? "" : dir + "/";
//循环将文件夹中的文件打包
for (int i = 0; i < files.length; i++) {
generateFile(out, files[i], dir + files[i].getName());
}
} else { // 当前是文件
// 输入流
FileInputStream inputStream = new FileInputStream(file);
// 标记要打包的条目
out.putNextEntry(new ZipEntry(dir));
// 进行写操作
int len = 0;
byte[] bytes = new byte[1024];
while ((len = inputStream.read(bytes)) > 0) {
out.write(bytes, 0, len);
}
// 关闭输入流
inputStream.close();
}
}
}
三、Excel工具类
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import cn.hutool.poi.excel.StyleSet;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.cache.MapCache;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.nm.entity.SheetData;
import com.nm.listener.UploadDataListener;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import java.io.*;
import java.util.List;
/**
* @Author: NanMing
* @Date: 2022/10//10:24
* @Description Excel工具类
*/
public class ExcelTool {
/**---------------所有读取文件方法------------------------------------------------------------------*/
/**
* 根据InputStream读取文件里的数据
* @param is
* @param sheetIndex 读取第几个sheet里的数据,可配合下方listSheet方法(获取所有sheet页)使用
* @return
*/
public static synchronized SheetData read(InputStream is, int sheetIndex) {
UploadDataListener arrayDataListener = new UploadDataListener();
//开启急速模式,开启后内存会在100M左右,但速度会快很多
EasyExcel.read(is, arrayDataListener).readCache(new MapCache()).sheet(sheetIndex).doRead();
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
return arrayDataListener.getData();
}
/**
* 默认读取路径下第一个sheet页数据
* @param path
* @return
* @throws FileNotFoundException
*/
public static SheetData read(String path) throws FileNotFoundException {
return read(path, 0);
}
/**
* 根据文件路径读取文件里的数据
* @param path
* @param sheetIndex 读取第几个sheet里的数据,可配合下方listSheet方法(获取所有sheet页)使用
* @return
* @throws FileNotFoundException
*/
public static SheetData read(String path, int sheetIndex) throws FileNotFoundException {
FileInputStream fis = null;
SheetData data = null;
try {
fis = new FileInputStream(path);
data = read(fis, sheetIndex);
} finally {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return data;
}
/**
* 获取excel文件所有sheet页
* @date 2022/3/15 17:13
* @param inputStream 文件流
* @return java.util.List<com.alibaba.excel.read.metadata.ReadSheet>
*/
public static List<ReadSheet> listSheet(InputStream inputStream){
if(inputStream == null){
throw new RuntimeException("inputStream is null");
}
ExcelReader build = EasyExcel.read(inputStream).build();
List<ReadSheet> readSheets = build.excelExecutor().sheetList();
return readSheets;
}
/**-----------------所有写文件方法--------------------------------------------------------------------*/
/**
* 下载文件到本地
* @param rows 数据集合:List<N行数据:List<一行数据>>
* @param tempDir 目标目录
* @param fileName 文件名
*/
public static void downFiles(List<List<String>> rows,String tempDir,String fileName){
ExcelWriter excelWriter = ExcelUtil.getWriter(true);
// 设置默认行高
excelWriter.setDefaultRowHeight(48);
//左对齐
StyleSet style = excelWriter.getStyleSet();
CellStyle headCellStyle = style.getHeadCellStyle();
headCellStyle.setAlignment(HorizontalAlignment.LEFT);
headCellStyle.setWrapText(true);
excelWriter.write(rows, true);
FileOutputStream outputStream = null;
try {
long time = System.currentTimeMillis();
outputStream = new FileOutputStream(tempDir+fileName+".xlsx");
excelWriter.flush(outputStream, true);
excelWriter.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//这些代码是通过浏览器下载
//response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
// response.setHeader("Content-Disposition","attachment;filename=test.xlsx");
//
// try {
// ServletOutputStream out = response.getOutputStream();
// writer.flush(out, true);
// writer.close();
// IoUtil.close(out);
// } catch (IOException e) {
// e.printStackTrace();
// }finally {
// writer.close();
//
// }
}
public static void write(String dist, SheetData data) {
write(dist, data, 0);
}
public static void write(String dist, SheetData data, int sheetIndex) {
ExcelWriterBuilder excelWriter = EasyExcel.write(dist);
excelWriter.head(data.toExcelHeader());
excelWriter.sheet(sheetIndex).doWrite(data.getDatas());
}
}
四、下面两个实例写的时间比较早,可做参考
2021-10-06 Excel导入实例:
public void upload(MultipartFile file) throws Exception{
InputStream is = file.getInputStream();
try {
Workbook wb = WorkbookFactory.create(is);
for (int numSheet = 0; numSheet <wb.getNumberOfSheets(); numSheet++) {
Sheet hssfSheet = wb.getSheetAt(numSheet);
if (hssfSheet == null) {
continue;
}
// 循环行Row
for (int rowNum = 1; rowNum <= hssfSheet.getLastRowNum(); rowNum++) {
Row hssfRow = hssfSheet.getRow(rowNum);
if (hssfRow != null) {
//第一行数据
Cell content=hssfRow.getCell(1);
//第二行数据
Cell content=hssfRow.getCell(1);
//....以此类推
}
}
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if (is != null) {
is.close();
}}
}
2021-10-06 Excel导出实例:
public void download(HttpServletRequest request, HttpServletResponse response) {
//从各种地方中读取数据
// 分割字符串
//把每一列的值放入list
List<String> 第一列=new ArrayList<>();List<String> 第二列=new ArrayList<>();
List<String> 第三列=new ArrayList<>();List<String> 第四列=new ArrayList<>();
//往上面4个里面加数据
List<List<String>> rows = new ArrayList<>();
for(int i=0;i< 第一列.size();i++){
List<String> row = CollUtil.newArrayList(, 第一列.get(i), 第二列.get(i)……等等);
rows.add(row);
}
List<List<String>> end = CollUtil.newArrayList(rows);
ExcelWriter writer = ExcelUtil.getWriter(true);
//第一列20长度,以下同理
writer.setColumnWidth(0,20);
writer.setColumnWidth(1,100);
writer.setColumnWidth(2,100);
// 设置默认行高
writer.setDefaultRowHeight(48);
//左对齐
StyleSet style = writer.getStyleSet();
CellStyle headCellStyle = style.getHeadCellStyle();
headCellStyle.setAlignment(HorizontalAlignment.LEFT);
headCellStyle.setWrapText(true);
writer.write(end, true);
// 第二个参数表示是否也设置头部单元格背景
style.setBackgroundColor(IndexedColors.TAN, false);
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
response.setHeader("Content-Disposition","attachment;filename=test.xlsx");
try {
ServletOutputStream out = response.getOutputStream();
writer.flush(out, true);
writer.close();
IoUtil.close(out);
} catch (IOException e) {
e.printStackTrace();
}finally {
writer.close();
}
}
样式设计可参考:https://apidoc.gitee.com/loolly/hutool/cn/hutool/poi/excel/ExcelWriter.html