注:网上找的资料
JXL:支持比较低版本的excel,比如Excel 95 ,97 ,2000,2003
由于Excel版本比较低,导致最大行有限制,无法导出65535以上量级的数据
对于内存,和时间的花费也比POI基于内存+磁盘的方式高。
网友技术说明
1. 读取Excel公式(可以读取Excel 97以后的公式)
2. 生成Excel数据表(格式为Excel 97)
3. 支持字体、数字、日期的格式化
4. 支持单元格的阴影操作,以及颜色操作
5. 修改已经存在的数据表
6. 是最基础的excel api
7. 小文件读取效率比较高
8. 跨平台
POI技术说明
总体来说,对于简单的单表excel导入导出的需求,建议使用JXL。数据量稍微小点,占用内存少,速度快。
对于报表类的,涉及月份数据量,多表数据聚合在一起建议使用POI。
poi的几个概念和Excel的关系
XSSFWorkbook
SXSSFWorkbook
HSSFWorkbook
HSSFSheet
SXSSFSheet
XSSFChartSheet
XSSFDialogsheet
XSSFCell
SXSSFCell
HSSFCell
XSSFCell:基于内存
SXSSFCell:基于内存+磁盘的写入方式
HSSFCell:高水平的写入方式,对于单元格的数据类型有很高的限制,比如:字符串格式只有字符,不能有数字
HSSFRow
SXSSFRow
XSSFRow
上面的每个抽象出来的对象都被POI封装好了,声明了接口,有不同的实现方式。
jxl的几个概念和Excel的关系
Poi导入excel
实体类GoodsInfoDto
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class GoodsInfoDto {
private static final long serialVersionUID = 1L;
/**
* 所属分类
*/
private String categoryId;
/**
* 物品名称
*/
private String goodsName;
/**
* 物品编码(手工对物品编码)
*/
private String goodsNo;
/**
* 条码(物品出厂时的条形码编号)
*/
private String barcode;
/**
* 单位(如:个、本等)
*/
private String unit;
/**
* 规格/型号(如:30cm*40cm,A4等)
*/
private String specification;
/**
* 单价(单位:分,使用时的单位是元,存储时乘以100,取出使用时除以100)
*/
private Integer price;
/**
* 存储位置
*/
private String storeLocation;
/**
* 品牌
*/
private String brand;
/**
* 颜色
*/
private String colour;
/**
* 当前库存数量
*/
private Integer pcs;
/**
* 分类对象
*/
private GoodsCategoryDto categoryDto;
/**
* 入库数
*/
private Integer addNumber;
/**
* 价格字符串
*/
private String priceStr;
/**
* 种类名称
*/
private String categoryName;
/**
* 备注
*/
// convert方法是在反射的类中的静态方法.也防止Excel中的表头
//和反射的类的字段顺序不一致,导致数据读取在不正确的属性上
public static String convert(String str){
switch (str){
case "物品分类": return "categoryName";
case "物品名称": return "goodsName";
case "物品编号": return "goodsNo";
case "条码": return "barcode";
case "单位": return "unit";
case "规格/型号": return "specification";
case "单价": return "priceStr";
case "品牌": return "brand";
case "颜色": return "colour";
case "当前库存数量": return "pcs";
case "储存位置": return "storeLocation";
case "备注": return "remarks";
default: return str;
}
}
}
导入的工具方法
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 导入excel文件
*/
public class ReadExcel {
private static final Logger logger = LoggerFactory.getLogger(ReadExcel.class);
/**
* @param file
* @param clazz
* @return java.util.List
* @author 86135
* @createTime 2021/9/7 15:27
* @description 读取excel文件的值
*/
public static List readExcel(MultipartFile file, Class clazz) {
List objectList = new ArrayList<>();
Workbook workbook = null;
InputStream inputStream = null;
try {
inputStream = file.getInputStream();
String originalFilename = file.getOriginalFilename();
String substring = originalFilename.substring(originalFilename.lastIndexOf(".") + 1);
//判断后缀是否为excel文件
if (substring.equals("xls") || substring.equals("xlsx")) {
//创建文本簿
workbook = WorkbookFactory.create(inputStream);
}
//获取第一个工作表
Sheet sheetAt = workbook.getSheetAt(0);
//获取数据的总行数
int lastRowNum = sheetAt.getLastRowNum();
//获取数据的总列数
int numberOfCells = sheetAt.getRow(0).getPhysicalNumberOfCells();
//获取表头
Row headRow = sheetAt.getRow(0);
//获取反射类的所有字段
Field[] declaredFields = getAllFields(clazz);
//创建一个字段数组,用于和excel执行顺序一致
Field[] fields = new Field[numberOfCells];
//获取静态方法,该方法适用于把excel表头映射成对应的实体类属性
Method covert = clazz.getDeclaredMethod("convert", String.class);
T t = clazz.newInstance();
for (int i = 0; i < numberOfCells; i++) {
for (Field field : declaredFields) {
Cell cell = headRow.getCell(i);
//按照excel中的存储存放数组,以便后面遍历excel表格,数据一一对应.
if (covert.invoke(t, getXCellVal(cell)).equals(field.getName())) {
fields[i] = field;
continue;
}
}
}
for (int x = 1; x < lastRowNum; x++) {
T object = clazz.newInstance();
//获得第i行对象
Row row = sheetAt.getRow(x);
//如果一行里的所有单元格都为空则不放进list里面
int a = 0;
for (int y = 0; y < numberOfCells; y++) {
if (!(row == null)) {
Cell cell = row.getCell(y);
if (cell == null) {
a++;
} else {
Field field = fields[y];
String value = getXCellVal(cell);
if (value != null && !value.equals("") && field != null) {
//给字段设置值.
setValue(field, value, object);
}
}
}
}
if (a != numberOfCells && row != null) {
objectList.add(object);
}
}
} catch (Exception e) {
logger.debug(e.getMessage(), e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
logger.debug(e.getMessage(), e);
}
}
}
return objectList;
}
/**
* 获取本类及其父类的属性的方法
* @param clazz 当前类对象
* @return 字段数组
*/
private static Field[] getAllFields(Class> clazz) {
List fieldList = new ArrayList<>();
while (clazz != null){
fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
clazz = clazz.getSuperclass();
}
Field[] fields = new Field[fieldList.size()];
return fieldList.toArray(fields);
}
/**
* @param field
* @param value
* @param object
* @return void
* @author 86135
* @createTime 2021/9/7 11:20
* @description 给字段赋值,判断值的类型,然后转换成实体类需要的类型
*/
private static void setValue(Field field, String value, Object object) {
try {
field.setAccessible(true);
DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
if (field.getGenericType().toString().contains("Integer")) {
field.set(object, Integer.valueOf(value));
} else if (field.getGenericType().toString().contains("String")) {
field.set(object, value);
} else if (field.getGenericType().toString().contains("Date")) {
field.set(object, fmt.parse(value));
}
field.setAccessible(false);
} catch (ParseException e) {
logger.debug(e.getMessage(), e);
} catch (IllegalAccessException e) {
logger.debug(e.getMessage(), e);
}
}
/**
* @param cell
* @return java.lang.String
* @author 86135
* @createTime 2021/9/7 11:03
* @description 获取单元格中的值
*/
private static String getXCellVal(Cell cell) {
DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
DecimalFormat df = new DecimalFormat("0.0000");
String val = "";
switch (cell.getCellType()) {
case XSSFCell.CELL_TYPE_NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
val = fmt.format(cell.getDateCellValue()); //日期型
} else {
val = df.format(cell.getNumericCellValue()); //数字型
// 去掉多余的0,如最后一位是.则去掉
val = val.replaceAll("0+?$", "").replaceAll("[.]$", "");
}
break;
case XSSFCell.CELL_TYPE_STRING: //文本类型
val = cell.getStringCellValue();
break;
case XSSFCell.CELL_TYPE_BOOLEAN: //布尔型
val = String.valueOf(cell.getBooleanCellValue());
break;
case XSSFCell.CELL_TYPE_BLANK: //空白
val = cell.getStringCellValue();
break;
case XSSFCell.CELL_TYPE_ERROR: //错误
val = "";
break;
case XSSFCell.CELL_TYPE_FORMULA: //公式
try {
val = String.valueOf(cell.getStringCellValue());
} catch (IllegalStateException e) {
val = String.valueOf(cell.getNumericCellValue());
}
break;
default:
val = cell.getRichStringCellValue() == null ? null : cell.getRichStringCellValue().toString();
}
return val;
}
}
在service层调用
List ts = ReadExcel.readExcel(file, GoodsInfoDto.class);
前端页面使用ajax请求传递参数
var upload = function () {
$.ajax({
url: "",
data: new FormData(document.getElementById('search_form')),
processData: false, // 告诉jquery要传输data对象
contentType: false, // 告诉jquery不需要增加请求头对于contentType的设置
dataType: 'json',
success : function(data) {
Dialog.msg("数据上传成功");
$("#searchlist").click();
}
});
}
// 文件上传
$("#file").on('change', function () {
var f = $("#file").val();
if (f === null || f === "") {
return Dialog.msg("上传文件不能为空")
} else {
var extname = f.substring(f.lastIndexOf(".") + 1, f.length);
extname = extname.toLowerCase();//处理了大小写
if (extname !== "xlsx" && extname !== "xls") {
return Dialog.msg("上传的文件后缀为xlsx或者xls");
}
}
upload();
});