利用POI实现电子表格导出/导入操作
1. 需要的依赖
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
2. 工具类
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
* Created with IntelliJ IDEA.
* User: 枫叶
* Date: 2020/12/22
* Description: excel读写工具类,需要poi-4.0.1.jar、poi-ooxml-4.0.1.jar、poi-ooxml-schemas-4.0.1.jar、xmlbeans-3.0.2.jar四个核心jar包
* 还需要commons-collections4-4.2.jar和commons-compress-1.18.jar两个辅助包
* Version: V1.0
*/
@SuppressWarnings("all")
public class POIUtil {
/**
* 根据fileType不同读取excel文件
*
* @param path 请求硬盘物理路径中的一个excel文件可以是xls,也可以是xlsx文件
* @return 返回数据列表
*/
public static List<List<String>> readExcelForPOI(InputStream ins, String fileType) throws IOException {
List<List<String>> lists = new ArrayList<>();
Workbook wb = null;
if ("xls".equals(fileType)) { // 判断是2003版本还是2007之后的版本,xls为2003版本,xlsx为2007版本
wb = new HSSFWorkbook(ins); // HSSFWorkbook类型对应2003版本
} else if ("xlsx".equals(fileType)) {
wb = new XSSFWorkbook(ins); // XSSFWorkbook类型对应2007之后版本
} else {
return null;
}
Sheet sheet = wb.getSheetAt(0); //假设读取第一个工作页sheet,index默认从0开始,如果存在多个sheet,那么需要循环Sheet判断
for (Row row : sheet) { //循环读取第一个工作页中的每一行记录
ArrayList<String> list = new ArrayList<>();
for (Cell cell : row) { // 循环读取一行中的每一列数据
cell.setCellType(CellType.STRING); // 根据不同类型转化成字符串
list.add(cell.getStringCellValue()); // 获取当前列中的数据
}
lists.add(list);
}
return lists;
}
public static List<List<String>> readExcelForPOI(String path) {
String fileType = path.substring(path.lastIndexOf(".") + 1); // 获取文件的后缀名
List<List<String>> lists = null; // 里面的list代表每一行数据,外面list代表所有行数据,实际项目中,需要把excel中的每一行数据做成POJO对象处理
InputStream is = null; // 生成输入流
try {
lists = readExcelForPOI(is, fileType);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return lists;
}
/**
* 创建Excel.xls 返回一个Workbook对象,外部调用方式:首先创建一个OutputStream对象,然后通过workbook.write(out);完成输出
*
* @param lists 需要写入excel的数据
* @param name 文件名
* @return 返回一个excel poi Workbook对象,如果没有解析成功,或者没有传入数据列表,则会返回null
* @throws IOException 如果IO失败会抛出异常
*/
public static Workbook creatExcelForPOI(List<List<String>> lists, String name) throws IOException {
/*
操作excel五个核心 1.Font字体 2.CellStyle样式单 3.Sheet页 4.Row行 5.Cell列
企业使用的是DOM4J、SAX完成xml解析,POI完成excel解析
DOM4J:(50M大小以下)解析机制,首先完整读取xml,把xml所有的内容放入了电脑内存中,操作性能极高,而且在使用读取规则更方便
SAX:解析机制,基于xml每一个节点位置来循环读取加载
POI:解析机制,类似于SAX,并且可以和DOM4J、SAX结合完成读写操作
*/
Workbook wb = new HSSFWorkbook(); // 创建2003 excel对象 HSSFWorkbook类型对应2003版本 XSSFWorkbook类型对应2007之后版本
Sheet sheet = wb.createSheet(name); // 创建第一个sheet(页),并命名,注意这里只创建一页,如果业务需求,可以添加多页
Font f = wb.createFont(); // 创建字体
f.setFontHeightInPoints((short) 10); // 创建字体样式:字体大小
f.setColor(IndexedColors.BLACK.getIndex()); // 创建字体样式:字体类型(这里设置的是黑体)
f.setBold(true); // 创建字体样式:粗体
CellStyle cs = wb.createCellStyle(); // 创建单元格每列格式对象
cs.setFont(f); // 把字体样式保存到样式单中
cs.setBorderLeft(BorderStyle.THIN); // 设置具有边框的效果:左边框
cs.setBorderRight(BorderStyle.THIN); // 设置具有边框的效果:右边框
cs.setBorderTop(BorderStyle.THIN); // 设置具有边框的效果:上边框
cs.setBorderBottom(BorderStyle.THIN); // 设置具有边框的效果:下边框
cs.setAlignment(HorizontalAlignment.CENTER); // 设置文字居中的效果
if (lists == null || lists.size() == 0) { // 如果没有传递数据列表,则直接返回null
return null;
}
for (int i = 0; i < lists.size(); i++) { // 设置每行每列的值 Row 行,Cell 方格 , Row 和 Cell 都是从0开始计数的
Row row01 = sheet.createRow(i); // 在当前sheet页上创建一个新行
List<String> listInner = lists.get(i);
for (int j = 0; j < listInner.size(); j++) {
sheet.setColumnWidth(j, 256 * 35); // 设置列宽。第一个参数表示要为第几列,第二个参数表示列的宽度,值为像素值。
Cell cell01 = row01.createCell(j); // 在row行上创建一列
cell01.setCellValue(listInner.get(j)); // 在此列上写入数据
cell01.setCellStyle(cs); // 在此列上添加样式
}
}
return wb;
}
}
3. 导出数据库中的记录
这里使用servlet演示,ssm和boot需将代码迁移到controller中
/**
* @author 枫叶
* @version 1.0
* @date 2020/12/23
*/
@WebServlet(value = {
"/download/yongHuList"
},
name = "download")
public class DownLoadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
YongHuService yongHuService = new YongHuServiceImpl();
//获取用户列表
List<YongHu> yongHuList = yongHuService.selectYhList();
List<List<String>> rowList = new ArrayList<>(yongHuList.size()+1);
List<String> title = new ArrayList<>(1);
title.add("用户名");
title.add("密码");
title.add("是否锁定");
title.add("部门");
title.add("权限");
rowList.add(title);
yongHuList.forEach(yongHu -> {
List<String> row = new ArrayList<>(5);
row.add(yongHu.getYongHuMing());
row.add(yongHu.getMiMa());
row.add(yongHu.getSuoDing() == 0 ? "否" : "是");
row.add(yongHu.getBuMen().getBmMing());
row.add(yongHu.getQuanXian().getQuanXianMing());
rowList.add(row);
});
Workbook workbook = POIUtil.creatExcelForPOI(rowList, "用户");
if (workbook != null) {
resp.setCharacterEncoding("UTF-8");
resp.setHeader("Content-Disposition", "attachment; filename=" + System.currentTimeMillis() + ".xls");
//获取响应报文输出流对象
ServletOutputStream out = resp.getOutputStream();
//输出
workbook.write(out);
out.flush();
out.close();
} else {
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().println(DataResponse.error("系统错误,或excel文件格式错误。"));
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
}
4. 上传excel文件并解析数据存到数据库
/**
* @author 枫叶
* @version 1.0
* @date 2020/12/23
*/
@WebServlet(value = {
"/upload/yongHuList"
},
name = "upload")
public class UpLoadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
YongHuService yongHuService = new YongHuServiceImpl();
BuMenService buMenService = new BuMenServiceImpl();
QuanXianService quanXianService = new QuanXianServiceImpl();
List<BuMen> buMenList = buMenService.chaXunBuMenList();
List<QuanXian> quanXianList = quanXianService.selectQuanXianList();
// 设置请求对象语言编码为UTF-8
req.setCharacterEncoding("UTF-8");
//固定写法
boolean isMultipart = ServletFileUpload.isMultipartContent(req);
if (isMultipart) {
// 创建工厂(这里用的是工厂模式)
DiskFileItemFactory factory = new DiskFileItemFactory();
// 获取ServletContext
ServletContext servletContext = req.getSession().getServletContext();
// 获取从ServletContext中得到上传来的数据,fileupload固定的参数:javax.servlet.context.tempdir
File repository = (File) servletContext.getAttribute("javax.servlet.context.tempdir");
// fileupload封装上传来的文件的各种属性(上传来的文件的大小、文件名等)
factory.setRepository(repository);
// fileupload生成对应的数据参数对象
ServletFileUpload upload = new ServletFileUpload(factory);
// 把request转成fileupload中FileItem的实例
List<FileItem> items = upload.parseRequest(req);
System.out.println(items);
FileItem item = items.get(0);
String fieldName = item.getName();
System.out.println("文件:"+fieldName+"读取成功");
int lastIndex = fieldName.lastIndexOf(".");
String houZhui = fieldName.substring(lastIndex + 1);
System.out.println(houZhui);
if (!("xls".equals(houZhui) || "xlsx".equals(houZhui))) {
resp.getWriter().println(DataResponse.error("只能传Excel文件"));
return;
}
InputStream fis = item.getInputStream();
List<List<String>> lists = POIUtil.readExcelForPOI(fis, houZhui);
if (lists == null || lists.size() == 0) {
resp.getWriter().println(DataResponse.error("文件中没有数据。"));
return;
}
List<YongHu> yongHuList = new ArrayList<>(lists.size() - 1);
for (int i = 1; i < lists.size(); i++) {
YongHu yongHu = new YongHu();
List<String> row = lists.get(i);
String col1 = row.get(0);
String col2 = row.get(1);
String col3 = row.get(2);
String col4 = row.get(3);
String col5 = row.get(4);
yongHu.setYongHuMing(col1);
yongHu.setMiMa(col2);
short suoDing = (short) ("是".equals(col3) ? 1 : 0);
yongHu.setSuoDing(suoDing);
//将部门名映射成部门id
int bmId = 0;
for (BuMen buMen : buMenList) {
if (buMen.getBmMing().equals(col5)) {
bmId = buMen.getBid();
break;
}
}
yongHu.setBmId(bmId);
//将权限名映射成权限id
int qxId = 0;
for (QuanXian quanXian : quanXianList) {
if (quanXian.getQuanXianMing().equals(col5)){
qxId=quanXian.getQxId();
break;
}
}
yongHu.setQxId(qxId);
yongHuList.add(yongHu);
}
//数据存入数据库
if (yongHuService.addYhList(yongHuList)) {
resp.getWriter().println(DataResponse.ok("成功导入" + yongHuList.size() + "条数据"));
return;
}
resp.getWriter().println(DataResponse.error("导入失败,请严格按照文件模板的格式写入EXCEL。"));
}
}
}
5. 上面用到的返回封装类DataResponse如下
public class DataResponse{
private final Map<String,Object> result;
@Override
public String toString() {
//这里也用到了fastJSON并且设置了日期的格式化格式
JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd hh:mm:ss";
return JSON.toJSONString(result,SerializerFeature.WriteDateUseDateFormat);
}
public DataResponse(Integer code,String msg,Object data){
result = new HashMap<>();
result.put("code",code);
result.put("msg",msg);
result.put("data",data);
}
public DataResponse(Integer code,String msg){
this(code,msg,null);
}
public DataResponse() {
result=new HashMap<>();
}
public DataResponse put(String key,Object value){
result.put(key,value);
return this;
}
public static DataResponse ok(){
return new DataResponse(200,"成功!");
}
public static DataResponse ok(String msg){
return new DataResponse(200,msg);
}
public static DataResponse ok(int code,String msg){
return new DataResponse(code,msg);
}
public static DataResponse ok(String msg,Object data){
return new DataResponse(200,msg,data);
}
public static DataResponse ok(int code,String msg,Object data){
return new DataResponse(200,msg,data);
}
public static DataResponse error(){
return new DataResponse(500,"服务器错误,操作失败!");
}
public static DataResponse error(String msg){
return new DataResponse(500,msg);
}
public static DataResponse error(int code,String msg){
return new DataResponse(code,msg);
}
public Object get(String key){
return result.get(key);
}
public Object getData(){
return result.get("data");
}
public void setCode(int code) {
result.put("code",code);
}
public void setMsg(String msg) {
result.put("msg",msg);
}
public void setData(Object data) {
result.put("data",data);
}
}