实现Excel的导入、导出
关于excel的操作在工作中经常会遇到,如果只是一次性使用的话,最简单的方式就是通过数据库的可视化工具(如Navicat)查询结果集之后直接一键生成excel了,当然这只能解燃眉之急,并不是长久之计。
首先Excel的导出是比导入简单的,在应用中导出也导出应用的多,导出主要分为前端导出和后端导出。
1. 通过前端方式导出Excel
前提读者你的前端项目是VUE的,如果不是可以看第二个。
- .安装依赖
npm install -S file-saver
npm install -S xlsx
npm install -D script-loader
- 导入Blob.js和Export2Excel.js文件
相当于我们要用的工具类了。
下载地址(https://pan.baidu.com/s/1mmzFl9U0jtQvBK0AjL0urg?pwd=83yc),提取码:83yc
( 笔者已经优化过Export2Excel.js了,之前其他地方下载存在过多列导致报错的问题,这里已经无需考虑了,直接拿来用就好) - 使用方式
将上面所说的js必须放到同一目录下
引入我们的工具类
import { export_json_to_excel2 } from "@/utils/Export2Excel";
在method里面添加两个方法
exportExcel() {
require.ensure([], () => {
const row = Object.keys(this.tableData[0]).sort().slice(0, -3);
const rowc = [].concat(row);
row.unshift("序号", "项目号码", "项目名称", "姓名");
rowc.unshift("index", "projectCode", "projectName", "userName");
const header = row;
// 上面设置Excel的表格第一行的标题
const filterVal = rowc;
const list = this.tableData; //把data里的tableData存到list
for (let i = 0; i < list.length; i++) {
list[i]["index"] = i + 1;
}
var header2 = [
"序号",
"项目号码",
"项目名称",
"姓名",
"项目工作量(原始工作量,人时)每人每天不超过7.5小时",
];
for (let j = 0; j < row.length; j++) {
header2.push("");
}
const multiHeader = [header2]; // 前一行的表头数据,二维数组,不够的用空白补全
const data = this.formatJson(filterVal, list);
const merges = ["A1:A2", "B1:B2", "C1:C2", "D1:D2", "E1:Q1"]; // 合并单元格的数据,如何合并参考上面图片讲解
export_json_to_excel2({
multiHeader,
header,
merges,
data,
filename: "工作量核算表",
});
});
},
formatJson(filterVal, jsonData) {
return jsonData.map((v) => filterVal.map((j) => v[j]));
},
准备我们的data数据(这只是笔者提供的例子,可以根据自己的需要去改变)
tableData: [
{"2021-09": 0,"2021-10": 75,"2021-11": 7.5,projectCode: "123",projectName: "测试1",userName: "小红",},
{"2021-09": 24.5,"2021-10": 62.5,"2021-11": 10.5,projectCode: "123",projectName: "测试1",userName: "小丽",},
],
调用我们的方法
//`方法:数据导出`
download() {
this.exportExcel();
},
- 实现效果
2. 通过后端方式导出Excel
为什么会有前端导出和后端导出呢?其实现在的系统很多都是前后端分离的,前端的人无需接触后台也能开发出一个导出功能(笔者是这么想的),后端导出其实就是POI,也是工作中用的比较多的,相对于前端导出,后端导出的安全性要高,处理的效率也比较高,能应用的场景更多,前提你得对POI有一定的了解。
1.引入pom(注:部分功能会因为poi的版本不同而有差异。)
<!-- poi -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
2.实现代码
@SuppressWarnings("resource")
@RequestMapping("/exportWorkloads")
public void exportWorkloads(HttpServletResponse response) throws Exception {
String[] tableHeaders = {"姓名", "工作日期", "工作小时数", "财务小时数", "工作内容"};
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet sheet = workbook.createSheet("Sheet1");
HSSFCellStyle cellStyle = workbook.createCellStyle();
cellStyle.setAlignment(HorizontalAlignment.CENTER);
cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
Font font = workbook.createFont();
font.setBold(true);
cellStyle.setFont(font);
// 将第一行的五个单元格给合并
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 4));
HSSFRow row = sheet.createRow(0);
HSSFCell beginCell = row.createCell(0);
//第一行的表头
String title = "测试测试";
beginCell.setCellValue(title);
beginCell.setCellStyle(cellStyle);
row = sheet.createRow(1);
// 创建第二行的表头
for (int i = 0; i < tableHeaders.length; i++) {
HSSFCell cell = row.createCell(i);
cell.setCellValue(tableHeaders[i]);
cell.setCellStyle(cellStyle);
}
//导出的表格数据
ArrayList<Map> maps = new ArrayList<>();
for (int i = 1; i < 4; i++) {
HashMap<String, String> map = new HashMap<>();
map.put("姓名","马云"+i);
map.put("工作日期","2022-07-0"+i);
map.put("工作小时数",String.valueOf(i));
map.put("财务小时数",String.valueOf(i));
map.put("工作内容","我是第"+i+"天的工作内容");
maps.add(map);
}
for (int i = 0, j = maps.size(); i < j; i++) {
//填充数据
row = sheet.createRow(i + 2);
Map<String,String> map = maps.get(i);
row.createCell(0).setCellValue(map.get("姓名"));
row.createCell(1).setCellValue(map.get("工作日期"));
row.createCell(2).setCellValue(map.get("工作小时数"));
row.createCell(3).setCellValue(map.get("财务小时数"));
row.createCell(4).setCellValue(map.get("工作内容"));
}
OutputStream outputStream = response.getOutputStream();
response.reset();
response.setContentType("application/vnd.ms-excel");
String filename = "我是导出的文件名";
//文件名要进行编码转换,默认是UTF8可能会显示异常
String basename = new String(filename.getBytes("gb2312"), "ISO8859-1");
response.setHeader("Content-disposition", "attachment;filename=" + basename + ".xls");
workbook.write(outputStream);
outputStream.flush();
outputStream.close();
}
3.实现效果
3. 导入Excel
导入Excel可以理解为从Excel读出数据,笔者使用的工具类来自于与Hutool,后端开发的应该都知道,这是功能强大的工具类了。
1.引入依赖
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.3</version>
</dependency>
2.准备我们的视图数据
3.前端上传文件
附上关键代码(使用什么组件都可以,笔者使用原生的input)
//组件
<input type="file" ref="feedbakcfile" @change="getFile($event)" />
//method
getFile(event) {
this.file = event.target.files[0];
},
//提交方法
exportUp() {
if (!this.file) {
this.notifyError("请选择文件上传");
return;
}
let formData = new FormData();
formData.append("file", this.file);
//根据你使用的技术写ajax,这里要注意header!
request({
url: "/eip/excel/read",
method: "post",
data: formData,
header: { "Content-Type": "multipart/form-data" },
})
.then((r) => {
if (r.success) {
this.notifySuccess("导入成功");
} else {
this.$message.error(r.message);
}
})
.catch((e) => {
this.notifyError((e || {}).message || "导入失败");
});
},
4.代码片段
/**
* 导入
*
* @param file
* @return
* @throws Exception
*/
@PostMapping("/read")
@ResponseBody
public Map<String, Object> read(MultipartFile file) throws Exception {
Map<String, Object> result = new HashMap<>();
//首先判断是不是空的文件
if (!file.isEmpty()) {
//对文文件的全名进行截取然后在后缀名进行删选。
int begin = file.getOriginalFilename().indexOf(".");
int last = file.getOriginalFilename().length();
//获得文件后缀名
String a = file.getOriginalFilename().substring(begin, last);
if (!(a.endsWith(".xlsx") || a.endsWith(".xls"))) {
result.put("success",false);
result.put("message","文件类型只能为xlsx、xls!");
return result;
}
} else {
result.put("success",false);
result.put("message","文件不能为空!");
return result;
}
byte[] byteArr = file.getBytes();
InputStream inputStream = new ByteArrayInputStream(byteArr);
ExcelReader reader = ExcelUtil.getReader(inputStream, true);
//从第3行读起,第一行默认是0
List<List<Object>> lists = reader.read(2);
if (lists.size() == 0) {
result.put("success",false);
result.put("message","数据为空。");
return result;
}
//目标数据
List<Map> maps = new ArrayList<>();
for (int i = 0, j = lists.size(); i < j; i++) {
List<Object> data = lists.get(i);
HashMap<String, String> map = new HashMap<>();
String date = ((DateTime) data.get(0)).toString("yyyy-MM-dd");
String name = data.get(1).toString();
String plan = data.get(2).toString();
String planTask = data.get(3).toString();
map.put("date",date);
map.put("name",name);
map.put("plan",plan);
map.put("planTask",planTask);
maps.add(map);
}
//可以打印看看maps是不是你想要的数据
result.put("success",true);
return result;
}