后台管理系统的excel导出功能,以及使用excel进行批量导入都是必不可少的功能,本篇主要介绍内容如下:
先上效果图:
通过使用poi进行excel文件的解析:
implementation 'org.apache.poi:poi:4.0.1'
implementation 'org.apache.poi:poi-ooxml:4.0.1'
版本不要太高容易报错,spring使用的基础包版本可能不支持
private static final String XLS = "xls";
private static final String XLSX = "xlsx";
public static Workbook getWorkbook(InputStream inputStream, String fileType) throws IOException {
Workbook workbook = null;
if (fileType.equalsIgnoreCase(XLS)) {
workbook = new HSSFWorkbook(inputStream);
} else if (fileType.equalsIgnoreCase(XLSX)) {
workbook = new XSSFWorkbook(inputStream);
}
return workbook;
}
Sheet sheet = workbook.getSheetAt(sheetId);
Row header = sheet.getRow(0);
if (header == null) {
log.warn("解析失败表头没有数据");
return null;
}
int columnNum = header.getPhysicalNumberOfCells();
String[] properties = new String[columnNum];
for (int i = 0; i < properties.length; i++) {
properties[i] = header.getCell(i).toString();
}
Map<String, Method> methods = Stream.of(clazz.getMethods())
.filter(method -> method.getName().startsWith("set"))
.collect(Collectors.toMap(Method::getName, it->it));
Map<String, Type> fieldMap = Stream.of(clazz.getDeclaredFields())
.collect(Collectors.toMap(Field::getName, Field::getType));
var objs = new ArrayList<>();
for (int i = startRowNum; i <= endRowNum; ++i) {
Row row = sheet.getRow(i);
if (row == null) continue;
Object obj = clazz.getDeclaredConstructor().newInstance();
try {
for (int j = 0; j < columnNum; j++) {
var methodName = "set" + properties[j].substring(0, 1).toUpperCase() + properties[j].substring(1);
if (methods.containsKey(methodName)){
Method method = methods.get(methodName);
Cell cell = row.getCell(j);
Type type = fieldMap.get(properties[j]);
method.invoke(obj,cell2Obj(cell, type));
}
}
} catch (Exception e) {
log.error("{}文件的第{}行解析出现错误,错误类型为{},错误内容{}", fileName, i, e.getClass(), e.getMessage());
}
objs.add(obj);
}
由于excel使用的原因,通过泛型处理会出现类型对不上的情况,比如一个数值,excel默认所有数值都是double
类型,如果你的对象中是Long的话,那么就要进行处理不然没法cast
private static Object cell2Obj(Cell cell, Type type) {
if (cell == null) return null;
Object value = null;
switch (cell.getCellType()) {
case NUMERIC:
if (type.getTypeName().equals("long")) value = Long.valueOf(cell.getStringCellValue());
else {
Double doubleValue = cell.getNumericCellValue();
DecimalFormat df = new DecimalFormat("0");
value = df.format(doubleValue);
}
break;
case STRING:
if (cell.getStringCellValue().equals("")) break;
switch (type.getTypeName()) {
case "long":
value = Long.valueOf(cell.getStringCellValue());
break;
case "java.time.LocalDateTime":
String date = cell.getStringCellValue().trim();
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
value = LocalDateTime.parse(date, df);
break;
case "java.util.List":
String data = cell.getStringCellValue();
value = Arrays.asList(data.substring(1, data.length() - 1).split(","));
break;
default:
value = cell.getStringCellValue();
break;
}
break;
case BOOLEAN:
value = cell.getBooleanCellValue();
break;
case FORMULA:
value = cell.getCellFormula();
break;
default:
break;
}
return value;
}
写入的简单步骤:
List<String> header = fields.stream()
.map(Field::getName)
.collect(Collectors.toList());
Workbook workbook = new SXSSFWorkbook();
Sheet sheet = buildDataSheet(workbook, header);
public static List<Field> getFieldsInfo(Class<?> clazz) {
Field[] fields = clazz.getDeclaredFields();
List<Field> list = new ArrayList<>(Arrays.asList(fields));
Class<?> superClazz = clazz.getSuperclass();
if (superClazz != null) {
Field[] superFields = superClazz.getDeclaredFields();
list.addAll(Arrays.asList(superFields));
}
return list;
}
private static CellStyle buildHeadCellStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setBorderBottom(BorderStyle.THIN);
style.setBottomBorderColor(IndexedColors.BLACK.getIndex());
style.setBorderLeft(BorderStyle.THIN);
style.setLeftBorderColor(IndexedColors.BLACK.getIndex());
style.setBorderRight(BorderStyle.THIN);
style.setRightBorderColor(IndexedColors.BLACK.getIndex());
style.setBorderTop(BorderStyle.THIN);
style.setTopBorderColor(IndexedColors.BLACK.getIndex());
style.setFillForegroundColor(IndexedColors.SKY_BLUE.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
Font font = workbook.createFont();
font.setBold(true);
style.setFont(font);
return style;
}
private static CellStyle buildNormalCellStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
//对齐方式设置
style.setAlignment(HorizontalAlignment.CENTER);
return style;
}
for (int i=0; i<header.size(); i++) {
sheet.setColumnWidth(i, 4000);
}
// 设置默认行高
sheet.setDefaultRowHeight((short) 400);
for (int i = 0; i < objs.size(); i++) {
Row row = sheet.createRow(i + 1);
Object obj = objs.get(i);
for (int j = 0; j < header.size(); j++) {
Cell cell = row.createCell(j);
var methodName = "get" + header.get(j).substring(0, 1).toUpperCase() + header.get(j).substring(1);
int index = methodNames.indexOf(methodName);
if (index != -1) {
Method method = methods.get(index);
Class<?> fieldType = fields.get(j).getType();
Object value = method.invoke(obj);
if (value != null && fieldType == LocalDateTime.class) {
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String date = df.format((LocalDateTime)value);
cell.setCellValue(date);
} else {
if (value == null) value = "";
cell.setCellValue(value.toString());
}
cell.setCellStyle(normalStyle);
}
}
}
OutputStream outputStream = new FileOutputStream(fileName);
if (workbook != null) {
workbook.write(outputStream);
workbook.close();
}
Path filePath = Files.createTempFile("",".xlsx");
生成一个tmp文件夹的文件,这里要.xlsx结尾不然打不开part.transferTo(filePath);
根据路径将filepart中的内容写到path文件List<Object> user = ReadExcelUtil.readExcel(in, file.getPath(), 0, SysUser.class);
解析excel文件生成数据@PostMapping(value = "/upload/excel", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ResponseStatus(value = HttpStatus.OK)
public Flux<SysUser> upload(@RequestPart("file") Flux<FilePart> filePart){
return filePart
.flatMap(part -> {
try {
Path filePath = Files.createTempFile("",".xlsx");
part.transferTo(filePath);
File file = new File(filePath.toString());
InputStream in = new FileInputStream(file);
List<Object> user = ReadExcelUtil.readExcel(in, file.getPath(), 0, SysUser.class);
if (user != null) return Mono.justOrEmpty(user);
in.close();
} catch (IOException e) {
log.error(e.getMessage());
}
return Mono.empty();
})
.flatMap(it -> Flux
.fromIterable(it)
.cast(SysUser.class)
);
}
showUploadList: false,
不显示进度条,上传文件setUserData(info.file.response);
done的时候通过hook将数据写到userdata中,然后传入tablename: 'file',
这里的name和后端对应 const [userData, setUserData] = useState([]);
const uploadProps = {
name: 'file',
action: 'http://localhost:8080/api/io/upload/excel',
headers: {
authorization: getToken(),
},
showUploadList: false,
onChange(info:any) {
if (info.file.status !== 'uploading') {
// console.log(info.file, info.fileList);
}
if (info.file.status === 'done') {
setUserData(info.file.response);
// console.log(info.file.response);
message.success(`${
info.file.name} file uploaded successfully`);
} else if (info.file.status === 'error') {
message.error(`${
info.file.name} file upload failed.`);
}
},
};
<Dragger {
...uploadProps}>
<p className="ant-upload-drag-icon">
<InboxOutlined />
</p>
<p className="ant-upload-text">点击或者拖拽文件进行上传</p>
</Dragger>
new String(("test-" + LocalDateTime.now().toLocalDate() + ".xlsx").getBytes(StandardCharsets.UTF_8),"iso8859-1");
这里避免文件名乱码@PostMapping("/download/excel/db")
public Mono<Void> downloadFromDb(ServerHttpResponse response) throws UnsupportedEncodingException {
String fileName = new String(("test-" + LocalDateTime.now().toLocalDate() + ".xlsx").getBytes(StandardCharsets.UTF_8),"iso8859-1");
File file = new File(fileName);
return sysUserService.findAll()
.collectList()
.flatMap(list -> WriteExcelUtil.data2Workbook(list, SysUser.class))
.flatMap(workbook-> {
try {
workbook.write(new FileOutputStream(file));
} catch (IOException e) {
e.printStackTrace();
}
return downloadFile(response, file, fileName);
});
}
private Mono<Void> downloadFile(ServerHttpResponse response, File file, String fileName) {
ZeroCopyHttpOutputMessage zeroCopyHttpOutputMessage = (ZeroCopyHttpOutputMessage) response;
try {
response.getHeaders()
.set(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=".concat(
URLEncoder.encode(fileName, StandardCharsets.UTF_8.displayName())));
return zeroCopyHttpOutputMessage.writeWith(file, 0, file.length());
} catch (UnsupportedEncodingException e) {
throw new UnsupportedOperationException();
}
}
responseType: 'arrayBuffer',
这里要选arrayBuffer,默认是jsonexport async function downloadExcel(filename:string) {
const token = `Bearer ${
localStorage.getItem('token')}`;
return request('http://localhost:8080/api/io/download/excel/db', {
method: 'post',
headers:{
'Authorization': token,
},
responseType: 'arrayBuffer',
})
.then(res => {
const blob = new Blob([res], {
type: "application/vnd.ms-excel"});
download(blob, filename);
})
}
export function download(blobData: Blob, forDownLoadFileName: string ): any {
const elink = document.createElement('a');
elink.download = forDownLoadFileName;
elink.style.display = 'none';
elink.href = URL.createObjectURL(blobData);
document.body.appendChild(elink);
elink.click();
URL.revokeObjectURL(elink.href); // 释放URL 对象
document.body.removeChild(elink);
}
github | 前端(antd pro) | 后端(spring webflux) |
---|---|---|
gitee | 前端(antd pro) | 后端(spring webflux) |
用sudo时提示"xxx is not in the sudoers file. This incident will be reported.其中XXX是你的用户名,也就是你的用户名没有权限使用sudo,我们只要修改一下/etc/sudoers文件就行了。下面是修改方 法:1)进入超级用户模式。也就是输入"su -",系统会让你输入超级用户密码,输入密码后就进入了超级用户模式。(当然,你...
**实现财务自由的重要工具**1.三大核心工具A.企业B.股票(指数基金)C.房地产(REITS):用好企业这个核心工具,会成为企业家;用好股票和房地产,会成为投资家用户企业,股票,房地产,会成为资本家2、三个辅助工具(在没有好的机会的时候,使用辅助工具)A、可转债B、逆回购C、货币基金3、一个保障工具(转移财务风险)A、保障型保险...
练习:使用递归计算1-n之间的和 定义一个方法,使用递归计算1-n之间的和 1+2+3+...+n n+(n-1)+(n-2)+...+1 已知: 最大值:n 最小值:1 使用递归必须明确: 1.递归的结束条件 获取到1的时候结束 2.递归的目的 获取下一个倍加的数字(n-1)_求一到n的累加和6次递归
string的c_str函数很怪异很危险, 先来看一个简单的例子:#include #include using namespace std;int main(){ string s = "abc"; const char *p = s.c_str(); cout << p << endl; // abc s = "xyz"; cout << p << endl; // _c_str()函数 崩溃原因
Vue关闭当前页面。_vue关闭当前页面
【实例简介】python urllib3 安装文件包【实例截图】【核心代码】urllib3-1.8.2.tar└── urllib3-1.8.2├── CHANGES.rst├── CONTRIBUTORS.txt├── dummyserver│ ├── certs│ │ ├── cacert.key│ │ ├── cacert.pem│ │ ├── client_ba..._proxy.pyc
【代码】我的vscode json 配置。_vscode 配置json
判断字符串是否包含数字、字母、符号3项组合的正则表达式 ,字符串长度为 8~16位var re = /^(?:(?=.*[0-9].*)(?=.*[A-Za-z].*)(?=.*[\W].*))[\W0-9A-Za-z]{8,16}$/; if (re.test(字符串)){}...
目录Fix Point conversion定点数据类型浮点数据类型硬件为什么需要定点化Fixed-Point ConversionCode generationSpeed and Area Optimization速度优化面积优化延迟平衡HDL Coder 的系统设计定点数据类型浮点数据类型硬件为什么需要定点化Fixed-Point ConversionCode generationFix Point conversion定点数据类型浮点数据类型硬件为什么需要定点化Fixed-Point Con_hdl coder
这个类是java里精确计算的类 1 比较对象是否相等 一般的对象用equals,但是BigDecimal比较特殊,举个例子: BigDecimal a=BigDecimal.valueOf(1.0); BigDecimal b=BigDecimal.valueOf(1.000); 在现实中这两个数字是相等的,但是问题来来了 a.equals(b)结果..._bigdecimal类型比较大小相等
解题思路:先用一个map统计出,每一个字符的最后出现位置。遍历字符串S,对于每一个字符,判断它的最后出现位置是否包含在 前面的字符最后出现位置内,如果不包含,就更新最后出现位置。每次达到最后出现位置时,就计算片段的长度,直到遍历结束为止。 public List<Integer> partitionLabels(String S) { ArrayList&l..._c++划分字母区间
一、什么是SambaSamba是在Linux和UNIX系统上实现SMB协议的一个免费软件,由服务器及客户端程序构成。SMB(Server Messages Block,信息服务块)是一种在局域网上共享文件和打印机的一种通信协议,它为局域网内的不同计算机之间提供文件及打印机等资源的共享服务。SMB协议是客户机/服务器型协议,客户机通过该协议可以访问服务器上的共享文件系统、打印机及其他资源。通过设置“NetBIOS over TCP/IP”使得Samba不但能与局域网络主机分享资源,还能与全世界的电脑分享资源_ubuntu samba