前两天做了导出excel还要求打开时输入密码,整理了一下代码,写个工具类。
说明:
- excel表格分为2003版本文件名后缀xls和2007版本文件名后缀xlsx,版本不同读写创建文档略有不同
- xls加密依赖jxcell.jar,xlsx依赖的poi-ooxml.jar,且两者不能混用否则报错。
- xls 自带了写保护功能,但可以绕过密码以只读模式打开文档,只是不能编辑而已。本次的需求是要密码才准查看,所以改用了jxcell.jar
- xssfWorkbook当数据超大(1048576行,16384列)时,会面临内存溢出的问题,可以换成SXSSFWorkbook(原理是分批写入磁盘),写法和xssfWorkbook类似。也可以使用阿里的easyexcel(见另外一篇文章)
- 工具类功能:加解密xls/xlsx、读取/写入xls/xlsx,数据量很大时候用writeBigDataExcel()
测试类
package ExcelTest;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* 测试类
*
* @author yulisao
* @createDate 2021年4月18日
*/
public class ExcelUtilTest{
public static void main(String[] args) {
String sheetName = "sheetName";
String outPath= "C:\\Users\\Administrator\\Desktop\\rr";
String password = "123456";
String [] header = {"序号","姓名","手机号"};
List> dataList = new ArrayList<>();
//用于存储一行数据
List
工具类
package ExcelTest;
import java.awt.Color;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackageAccess;
import org.apache.poi.poifs.crypt.EncryptionInfo;
import org.apache.poi.poifs.crypt.EncryptionMode;
import org.apache.poi.poifs.crypt.Encryptor;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.xssf.streaming.SXSSFCell;
import org.apache.poi.xssf.streaming.SXSSFRow;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFColor;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import com.jxcell.CellException;
import com.jxcell.View;
/**
* 导出excel并加密工具类
* xls 设置密码依赖jxcell.jar
* xlsx 设置密码依赖poi-ooxml.jar(部分版本不支持)
* 两种文件的加解密方法,二者不能混用
*
* @author yulisao
* @createDate 2021年4月18日
*/
public class ExcelUtil {
private static final String TEMP_XLS_PATH = System.getProperty("java.io.tmpdir") + "temp.xls"; // 系统临时文件
private static final String TEMP_XLSX_PATH = System.getProperty("java.io.tmpdir") + "temp.xlsx";
private static final int ROW_LIMT = 10000; // 分次导出单次上限
/**
* 导出excel
*
* @param sheetName 文件名称
* @param header 标题行数组
* @param dataList 数据列表
* @param outDir 输出目录
* @param password 加密密码
*/
public static void writeXlsxExcel(String sheetName, String [] header, List> dataList, String outDir, String password) {
//创建工作薄
XSSFWorkbook workbook = new XSSFWorkbook();
//创建表单
XSSFSheet sheet = workbook.createSheet(sheetName);
//创建表单样式
XSSFCellStyle titleStyle = getCellStyle(workbook, true, true);//创建标题样式
XSSFCellStyle contextStyle = getCellStyle(workbook, true, false);//创建文本样式
XSSFRow row = sheet.createRow(0);//创建第一行,从0开始
XSSFCell cell ;//创建列
// 第一行标题行
for (int i = 0; i < header.length; i++) {
sheet.setColumnWidth(i, 4000);//统一设置列宽
cell = row.createCell(i);//标题行第i列
cell.setCellValue(header[i]);//标题第i列内容
cell.setCellStyle(titleStyle);//设置标题样式
}
//从数据库取数据填充到Excel
for(int i = 1 ; i < dataList.size() + 1; i++){// 标题行已占据1行,从1开始,
row=sheet.createRow(i);//创建第i行
List data = dataList.get(i-1);
for (int j = 0; j < data.size(); j++) {
cell=row.createCell(j);//正文第j列
cell.setCellValue(StringUtils.isBlank(data.get(j).toString())?"":data.get(j).toString());//标题第j列的值
cell.setCellStyle(contextStyle);//设置样式
}
}
// 文件夹不存在则创建
String outPath = outDir + File.separator + sheetName + ".xlsx";
File outFile = new File(outDir);
if (!outFile.exists()) {
outFile.mkdir();
}
//将工作薄写入文件输出流中
try {
workbook.write(new FileOutputStream(new File(outPath)));
} catch (IOException e1) {
System.out.println("导出文件失败");
e1.printStackTrace();
return;
}
// 加密
if (StringUtils.isNotBlank(password)) {
entryXlsxExcel(outPath, password);
}
}
/**
* 读取excel文档(支持xls、xlsx)
*
* @param filePath 文件路径
* @param begRow 开始读取行(可过滤掉标题行)
* @param password 文档密码
* @return
*/
public static List> readExcel(String filePath, int begRow, String password) {
if (begRow < 0) { // 默认第一行开始读取
begRow = 0;
}
Workbook wb = getWorkbook(filePath, password); // 读取文档内容
Sheet sheet = wb.getSheetAt(0); // 读取第一个文档
List> dataList = new ArrayList>();
int numberOfRows = sheet.getPhysicalNumberOfRows(); // 总行数
for (int i = begRow; i < numberOfRows; i++) {
Row row = sheet.getRow(i); // 第i行
List list = new ArrayList();
int physicalNumberOfCells = row.getPhysicalNumberOfCells(); // 总列数
for (int j = 0; j < physicalNumberOfCells; j++) {
//Cell cell = row.getCell(j); // 第i行第j列单元格
String cell = row.getCell(j).getStringCellValue();
list.add(cell);
}
dataList.add(list);
}
return dataList;
}
/**
* 创建单元格样式
*
* @param workbook
* @param border 是否添加边框
* @param bold 是否加粗
* @return
*/
public static XSSFCellStyle getCellStyle(XSSFWorkbook workbook, boolean border, boolean bold){
XSSFCellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);//文本水平居中显示
style.setVerticalAlignment(VerticalAlignment.CENTER);//文本竖直居中显示
style.setWrapText(true);//文本自动换行
// 设置边框
if (border) {
style.setBorderBottom(BorderStyle.THIN);//设置文本边框
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
style.setBorderTop(BorderStyle.THIN);
style.setTopBorderColor(new XSSFColor(Color.BLACK));//设置文本边框颜色
style.setBottomBorderColor(new XSSFColor(Color.BLACK));
style.setLeftBorderColor(new XSSFColor(Color.BLACK));
style.setRightBorderColor(new XSSFColor(Color.BLACK));
}
// 加粗
if (bold) {
XSSFFont titleFont = workbook.createFont();
titleFont.setBold(true);//加粗
titleFont.setFontHeight((short)13);//文字尺寸
titleFont.setFontHeightInPoints((short)13);
style.setFont(titleFont);
}
return style;
}
/**
* 加密xlsx文件
*
* @param outPath 文件路径
* @param password 密码
*/
public static void entryXlsxExcel(String outPath, String password) {
try {
POIFSFileSystem fs = new POIFSFileSystem();
EncryptionInfo info = new EncryptionInfo(EncryptionMode.standard);
Encryptor enc = info.getEncryptor();
//设置密码
enc.confirmPassword(password);
//加密文件
OPCPackage opc = OPCPackage.open(new File(outPath), PackageAccess.READ_WRITE);
OutputStream os = enc.getDataStream(fs);
opc.save(os);
opc.close();
//把加密后的文件写回到流
FileOutputStream fos = new FileOutputStream(outPath);
fs.writeFilesystem(fos);
fos.close();
} catch (Exception e) {
System.out.println("加密文件失败");
e.printStackTrace();
}
}
/**
* 解密xlsx文件
*
* @param outPath 文件路径
* @param password 密码
*/
public static void detryXlsxExcel(String outPath, String password) {
Workbook wb = null;
try {
FileInputStream in = new FileInputStream(outPath);
wb = WorkbookFactory.create(in,password);//设置密码打开
FileOutputStream fileOut = new FileOutputStream(new File(outPath));
wb.write(fileOut);
fileOut.close();
} catch (Exception e) {
System.out.println("解密文件失败");
e.printStackTrace();
}
}
public static void writeXlsExcel(String fileName, String [] header, List> dataList, String outPath, String password) {
// 创建workbook
HSSFWorkbook wb = new HSSFWorkbook();
// 创建sheet
HSSFSheet sheet = wb.createSheet("sheet1");
sheet.setDefaultColumnWidth(15); // 设置所有列宽
//sheet.setColumnWidth(2, 15 * 256); // 第3列设置列宽 15
// 创建行row
HSSFCellStyle style = wb.createCellStyle();
FileOutputStream fileOut = null;
HSSFRow row = sheet.createRow(0); // 创建第一行: 表头
// 表头的列逐个给值进去
for (int i = 0; i < header.length; i++) {
sheet.setColumnWidth(i, 4000);//统一设置列宽
HSSFCell cell = row.createCell(i);
cell.setCellValue(header[i]);
cell.setCellStyle(style);
}
// 插入数据正文
int num = 1; // 表头已占一行,所以这里从1开始
for (List list : dataList) { // 循环数据行
row = sheet.createRow(num); // 新建一行
for (int i = 0; i < list.size(); i++) { // 循环每一行的列
row.createCell(i).setCellValue(String.valueOf(list.get(i)));// 第i列
}
num++;
}
// 输出
String path = outPath + File.separator + fileName + ".xls";
try {
fileOut = new FileOutputStream(new File(path));
wb.write(fileOut);
fileOut.close();
} catch (IOException e) {
System.out.println("e1=" + e);
} finally {
try {
fileOut.close();
} catch (IOException e2) {
System.out.println("e2=" + e2);
}
}
if (StringUtils.isNotBlank(password)) {
entryXlsExcel(path, password);
}
}
/**
* 加密xls文件
*
* @param outPath 文件路径
* @param password 密码
*/
public static void entryXlsExcel(String outPath, String password) {
View view = new View();
try {
// 读取Excel文档
view.read(outPath);
// 设置文档打开密码
view.write(outPath, password);
} catch (CellException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 解密xls文件
*
* @param outPath 文件路径
* @param password 密码
*/
public static void detryXlsExcel(String outPath, String password) {
View view = new View();
try {
//尝试用错误的打开密码读文件
view.read(outPath, password);
view.write(TEMP_XLS_PATH); // 先写入临时文件夹,再移动回来
File outFile = new File(outPath);
outFile.delete(); // 调用此方法前outPath不要被输入输出流及其他程序占用,否则删除不成功
File tempFile = new File(TEMP_XLS_PATH);
tempFile.renameTo(outFile); // 原文件未删除,则无法移回同名文件
} catch (Exception e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
}
/**
* 获取excel内容
*
* @param filePath 路径
* @param passWord 密码
* @return
*/
public static Workbook getWorkbook(String filePath, String passWord){
Workbook workbook = null;
try {
if (StringUtils.endsWithIgnoreCase(filePath,".xls")){
if (StringUtils.isNotBlank(passWord)) {
detryXlsExcel(filePath, passWord);
}
POIFSFileSystem pfs = new POIFSFileSystem(new FileInputStream(new File(filePath))); // 2003版本
workbook = new HSSFWorkbook(pfs,true);
}else if (StringUtils.endsWithIgnoreCase(filePath,".xlsx")){
FileInputStream inputStream = new FileInputStream(new File(filePath));
if (StringUtils.isBlank(passWord)) {
workbook = new XSSFWorkbook(inputStream); // 2007版本
} else {
workbook = WorkbookFactory.create(inputStream, passWord);//设置密码打开
}
}
} catch (IOException | EncryptedDocumentException
| InvalidFormatException e) {
e.printStackTrace();
}
return workbook;
}
/**
* 导出excel(适用数据量超大,大于10w行时候)
*
* @param sheetName 文件名称
* @param header 标题行数组
* @param dataList 数据列表
* @param outDir 输出目录
* @param password 加密密码
*/
public static void writeBigDataExcel(String sheetName, String [] header, List> dataList, String outDir, String password) {
// 文件夹不存在则创建
String outPath = outDir + File.separator + sheetName + ".xlsx";
File outFile = new File(outDir);
if (!outFile.exists()) {
outFile.mkdir();
}
// 创建XSSFWorkbook输出
BufferedOutputStream outputStream = null;
XSSFWorkbook workbook = null;
try {
outputStream = new BufferedOutputStream(new FileOutputStream(new File(outPath)));
workbook = new XSSFWorkbook();
workbook.createSheet(sheetName);
workbook.write(outputStream);
} catch (Exception e) {
e.printStackTrace();
}finally {
if(outputStream!=null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
SXSSFWorkbook sxssfWorkbook = null;
try {
// 加载ROW_LIMT条到内存,其他输出到磁盘
sxssfWorkbook = new SXSSFWorkbook(workbook, ROW_LIMT);
// 获取第一个Sheet页
SXSSFSheet sheet = sxssfWorkbook.getSheetAt(0);
// 创建表头行
SXSSFRow row = sheet.createRow(0);
SXSSFCell cell ;//创建列
// 设置行样式
XSSFCellStyle titleStyle = getCellStyle(workbook, true, true);//创建标题样式
XSSFCellStyle contextStyle = getCellStyle(workbook, true, false);//创建文本样式
// 表头的列逐个给值进去
for (int i = 0; i < header.length; i++) {
sheet.setColumnWidth(i, 4000);// 统一设置列宽
cell = row.createCell(i);// 标题行第i列
cell.setCellValue(header[i]);// 标题第i列内容
cell.setCellStyle(titleStyle);// 设置标题样式
}
// 写入数据
for (int i = 1; i < dataList.size() + 1; i++) {// 标题行已占据1行,从1开始,
row = sheet.createRow(i);// 创建第i行
List data = dataList.get(i - 1);
for (int j = 0; j < data.size(); j++) {
cell=row.createCell(j);//正文第j列
cell.setCellValue(StringUtils.isBlank(data.get(j).toString())?"":data.get(j).toString());//标题第j列的值
cell.setCellStyle(contextStyle);//设置样式
}
}
outputStream = new BufferedOutputStream(new FileOutputStream(outPath)); // 读取文件
sxssfWorkbook.write(outputStream); // 写入
outputStream.flush();
sxssfWorkbook.dispose();// 释放资源
} catch (IOException e) {
e.printStackTrace();
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 加密
if (StringUtils.isNotBlank(password)) {
entryXlsxExcel(outPath, password);
}
}
}