美文网首页
java 导入导出Excel 坑位

java 导入导出Excel 坑位

作者: 客观开发者 | 来源:发表于2022-08-08 14:28 被阅读0次

    说明

    导入和导出这里有四种方案,具体写三种。
    1、apache 中poi 的使用
    2、easypoi 的使用
    3、alibaba 的 easyexcel
    4、hutool 工具中

    apache - poi 介绍

    官方主页: http://poi.apache.org/index.html
    文档: http://poi.apache.org/apidocs/index.html
    常用的api 类
    HSSF - 提供读写Microsoft Excel XLS格式档案的功能。
    XSSF - 提供读写Microsoft Excel OOXML XLSX格式档案的功能。
    HWPF - 提供读写Microsoft Word DOC97格式档案的功能。
    XWPF - 提供读写Microsoft Word DOC2003格式档案的功能。
    HSLF - 提供读写Microsoft PowerPoint格式档案的功能。
    HDGF - 提供读Microsoft Visio格式档案的功能。
    HPBF - 提供读Microsoft Publisher格式档案的功能。
    HSMF - 提供读Microsoft Outlook格式档案的功能。

    文档前 四个步骤就可以的


    image.png

    我只写了一个demo 导入和导出功能。

    <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi</artifactId>
                <version>5.0.0</version>
                <scope>compile</scope>
            </dependency>
    

    utils 方法

    
    public class ExcelUtils {
    
        /**
         * 导出Excel
         *
         * @param sheetName sheet名称
         * @param title     标题
         * @param values    内容
         * @param wb        HSSFWorkbook对象
         * @return
         */
        public static HSSFWorkbook getHSSFWorkbook(String sheetName, String[] title, String[][] values, HSSFWorkbook wb) {
            // 第一步,创建一个HSSFWorkbook,对应一个Excel文件
            if (wb == null) {
                wb = new HSSFWorkbook();
            }
            // 第二步,在workbook中添加一个sheet,对应Excel文件中的sheet
            HSSFSheet sheet = wb.createSheet(sheetName);
            // 第三步,在sheet中添加表头第0行,注意老版本poi对Excel的行数列数有限制
            HSSFRow row = sheet.createRow(0);
            // 第四步,创建单元格,并设置值表头 设置表头居中
            HSSFCellStyle style = wb.createCellStyle();
            style.setAlignment(HorizontalAlignment.CENTER); // 创建一个居中格式
            //声明列对象
            HSSFCell cell = null;
            //创建标题
            for (int i = 0; i < title.length; i++) {
                cell = row.createCell(i);
                cell.setCellValue(title[i]);
                cell.setCellStyle(style);
            }
            //创建内容
            for (int i = 0; i < values.length; i++) {
                row = sheet.createRow(i + 1);
                for (int j = 0; j < values[i].length; j++) {
                    //将内容按顺序赋给对应的列对象
                    row.createCell(j).setCellValue(values[i][j]);
                }
            }
            return wb;
        }
    }
    

    剩下就是命名和数据操作了。
    读取方法也和HSSFWorkbook 有关系。他既可以写也可以读

    
    @RestController
    public class TestController {
    
        /**
         * 导出报表
         *
         * @return
         */
        @RequestMapping(value = "/export")
        @ResponseBody
        public void export(HttpServletResponse response) {
            //获取数据
            List<PageData> list = new ArrayList<>();
    
            for (int i = 0; i < 10; i++) {
                PageData pageData = new PageData();
                pageData.setAge(i);
                pageData.setName("姓名" + i);
                pageData.setSex(i);
                pageData.setClassName("班级" + i + "版本");
                pageData.setSchool("学校" + i);
                list.add(pageData);
            }
    
            //excel标题
            String[] title = {"名称", "性别", "年龄", "学校", "班级"};
            //excel文件名
            String fileName = "学生信息表" + System.currentTimeMillis() + ".xls";
            //sheet名
            String sheetName = "学生信息表";
            String[][] content = new String[list.size()][];
            for (int i = 0; i < list.size(); i++) {
                content[i] = new String[title.length];
                PageData obj = list.get(i);
                content[i][0] = obj.getName();
                content[i][1] = "" + obj.getSex();
                content[i][2] = "" + obj.getAge();
                content[i][3] = obj.getSchool();
                content[i][4] = obj.getClassName();
            }
            //创建HSSFWorkbook
            HSSFWorkbook wb = ExcelUtils.getHSSFWorkbook(sheetName, title, content, null);
            //响应到客户端
            try {
                this.setResponseHeader(response, fileName);
                OutputStream os = response.getOutputStream();
                wb.write(os);
                os.flush();
                os.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        //发送响应流方法
        public void setResponseHeader(HttpServletResponse response, String fileName) {
            try {
                try {
                    fileName = new String(fileName.getBytes(), "ISO8859-1");
                } catch (UnsupportedEncodingException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                response.setContentType("application/octet-stream;charset=ISO8859-1");
                response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
                response.addHeader("Pargam", "no-cache");
                response.addHeader("Cache-Control", "no-cache");
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    
        @SuppressWarnings("resource")
        @RequestMapping("/import")
        public Object importExcel(@RequestParam("file") MultipartFile file) throws Exception {
            InputStream inputStream = file.getInputStream();
            BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
            POIFSFileSystem fileSystem = new POIFSFileSystem(bufferedInputStream);
            HSSFWorkbook workbook = new HSSFWorkbook(fileSystem);
            HSSFSheet sheet = workbook.getSheetAt(0);
    
            int lastRowNum = sheet.getLastRowNum();
            List<PageData> list = new ArrayList<>();
            for (int i = 1; i <= lastRowNum; i++) {
                HSSFRow row = sheet.getRow(i);
                String name = row.getCell(0).getStringCellValue();
                int sex = Integer.parseInt(row.getCell(1).getStringCellValue());
                int age = Integer.parseInt(row.getCell(2).getStringCellValue());
                String school = row.getCell(3).getStringCellValue();
                String className = row.getCell(4).getStringCellValue();
    
                PageData pageData = new PageData();
                pageData.setSchool(school);
                pageData.setClassName(className);
                pageData.setSex(sex);
                pageData.setName(name);
                pageData.setAge(age);
                list.add(pageData);
    
                System.out.println(name + "-" + sex + "-" + age + "-" + school + "-" + className);
            }
            return list;
        }
    }
    

    多取 多行的时候,需要从第二行开始的


    image.png

    更多功能可以去挖掘。

    image.png

    easypoi 介绍

    image.png
    <dependency>
                <groupId>cn.afterturn</groupId>
                <artifactId>easypoi-spring-boot-starter</artifactId>
                <version>4.1.2</version>
            </dependency>
    
    

    他demo 项目是独立的
    https://gitee.com/lemur/easypoi-test
    里面还有报错,用dev 就可以,master 去掉错误注释也可以用。
    好处就是导入和导出都有utils

    image.png

    我的demo

    package com.spring.easypoi.controller;
    
    import cn.afterturn.easypoi.excel.ExcelExportUtil;
    import cn.afterturn.easypoi.excel.ExcelImportUtil;
    import cn.afterturn.easypoi.excel.entity.ExportParams;
    import cn.afterturn.easypoi.excel.entity.ImportParams;
    import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
    import cn.hutool.core.io.FileUtil;
    import com.spring.easypoi.domain.UserDTO;
    import com.spring.easypoi.utils.ExcelUtils;
    import org.apache.poi.ss.usermodel.Workbook;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.multipart.MultipartFile;
    
    import javax.servlet.http.HttpServletResponse;
    import java.io.File;
    import java.io.IOException;
    import java.text.SimpleDateFormat;
    import java.util.*;
    
    @RestController
    public class PoiController {
    
        /**
         * 导出表格 - 最基础的
         *
         * @param response
         */
        @GetMapping("/export")
        public void export(HttpServletResponse response) {
            List<UserDTO> list = new ArrayList<>();
            for (int i = 0; i < 100; i++) {
                UserDTO client = new UserDTO();
                client.setUsername("小明" + i);
                client.setBirthday("18797" + i);
                client.setAge("1" + i);
                list.add(client);
            }
            ExportParams params = new ExportParams();
            params.setSheetName("测试");
            params.setType(ExcelType.XSSF);
    //        params.setFreezeCol(2);// 表头是否固定
            Workbook workbook = ExcelExportUtil.exportExcel(params, UserDTO.class, list);
    
            String now = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(new Date());
            String fileName = String.format("%s.xlsx", now);
            ExcelUtils.downLoadExcel(fileName, response, workbook);
        }
    
        @PostMapping("/import")
        public Object importXlsx(@RequestParam MultipartFile file) {
            /*
             * 服务器地址
             */
            String property = System.getProperty("user.dir");
            String OriginalFilename = file.getOriginalFilename();//获取原文件名
            String suffixName = OriginalFilename.substring(OriginalFilename.lastIndexOf("."));//获取文件后缀名
            //重新随机生成名字
            String filename = UUID.randomUUID().toString().replace("-", "") + suffixName;
            File localFile = new File(property + "\\" + filename);
            try {
                /*
                 * 上传方式1
                 * 把上传的文件保存至本地
                 */
    //            file.transferTo(localFile);
                /*
                 * 上传方式2
                 * 把上传的文件保存至本地
                 */
                FileUtil.writeBytes(file.getBytes(), localFile.getAbsoluteFile());
    
                ImportParams params = new ImportParams();
                List<Map<String, Object>> list = ExcelImportUtil.importExcel(localFile, Map.class, params);
    
                for (Map map : list) {
                    System.out.println(map.get("姓名"));
                    System.out.println(map.get("年龄"));
                    System.out.println(map.get("生日"));
                }
                /*
                 * 删除本地缓存数据
                 */
                localFile.delete();
                return list;
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("上传失败");
                return "" + e.getMessage();
            }
        }
    
    
    }
    
    

    多写了一个

    /**
         * 下载表格
         *
         * @param fileName
         * @param response
         * @param workbook
         */
        public static void downLoadExcel(String fileName, HttpServletResponse response, Workbook workbook) {
            try {
                response.setCharacterEncoding("UTF-8");
                response.setHeader("content-Type", "application/vnd.ms-excel");
                response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
                workbook.write(response.getOutputStream());
            } catch (IOException e) {
                e.getMessage();
            }
        }
    

    他的注解也不少


    image.png

    Map导入,自由发挥

    全部内容
    http://easypoi.mydoc.io/#text_202983
    http://doc.wupaas.com/docs/easypoi/easypoi-1c0u4mo8p4ro8

    easyexcel 介绍

    EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。 github地址:https://github.com/alibaba/easyexcel

    最简单的写

    <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>easyexcel</artifactId>
                <version>3.0.5</version>
            </dependency>
    
     /**
         * 最简单的写
         * <p>1. 创建excel对应的实体对象 参照{@link com.alibaba.easyexcel.test.demo.write.DemoData}
         * <p>2. 直接写即可
         */
        @Test
        public void simpleWrite() {
            String fileName = TestFileUtil.getPath() + "write" + System.currentTimeMillis() + ".xlsx";
            // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
            // 如果这里想使用03 则 传入excelType参数即可
            EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
        }
    

    最简单的读

        /**
         * 指定列的下标或者列名
         *
         * <p>1. 创建excel对应的实体对象,并使用{@link ExcelProperty}注解. 参照{@link IndexOrNameData}
         * <p>2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link IndexOrNameDataListener}
         * <p>3. 直接读即可
         */
        @Test
        public void indexOrNameRead() {
            String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
            // 这里默认读取第一个sheet
            EasyExcel.read(fileName, IndexOrNameData.class, new IndexOrNameDataListener()).sheet().doRead();
        }
    

    这里IndexOrNameDataListerner()这个是自定义,里面的方法如下

    
    public class IndexOrNameDataListener implements ReadListener<UserInfo> {
    
        @Override
        public void onException(Exception exception, AnalysisContext context) throws Exception {
            /*读取异常*/
        }
    
        @Override
        public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
    //        获取excel的第一行head数据
        }
    
        @Override
        public void invoke(UserInfo userInfo, AnalysisContext analysisContext) {
    //        读取正文数据,一次只读取一行
        }
    
        @Override
        public void extra(CellExtra extra, AnalysisContext context) {
    //        额外单元格返回的数据,这个方法还没详细了解过,一直没用到过
        }
    
        @Override
        public void doAfterAllAnalysed(AnalysisContext analysisContext) {
    //        所有数据读取完了
        }
    
        @Override
        public boolean hasNext(AnalysisContext context) {
    //        是否读取下一行
            return true;
        }
    }
    

    我的demo

    
    @RestController
    public class TestController {
    
        @GetMapping("/export")
        public Object index(HttpServletResponse response) {
            List<UserInfo> list = new ArrayList<>();
            for (int i = 1; i < 10; i++) {
                UserInfo userInfo = new UserInfo();
                userInfo.setId(i);
                userInfo.setUsername("姓名" + i);
                userInfo.setPassword("123456");
                userInfo.setAddress("地址" + i);
                userInfo.setPhone("电话" + i);
                list.add(userInfo);
            }
    
            try (ServletOutputStream out = response.getOutputStream()) {
                String disposition = "attachment; fileName=" +
                        new String("用户信息".getBytes("UTF-8"), "ISO-8859-1") + ".xls";
                response.setCharacterEncoding("utf8");
                response.setContentType("text/xls");
                response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode("用户信息表格" + ".xls", "UTF-8"));
                response.setHeader("Content-Disposition", disposition);
                EasyExcel.write(out, UserInfo.class).sheet("用户信息").doWrite(list);
                out.flush();
            } catch (IOException e) {
                throw new RuntimeException();
            }
            return "导出成功";
        }
    
        /**
         * 2、EasyExcel excel导入(百万级数据测试)
         * @return
         */
        @PostMapping("/submitExcel")
        public void submitExcel(MultipartFile file){
            //创建reader
            ExcelReader excelReader = null;
            try {
                excelReader = EasyExcel.read(file.getInputStream(), UserInfo.class, new ExcelUpload()).build();
                // 构建sheet,可以指定是第几个sheet
                ReadSheet readSheet = EasyExcel.readSheet(0).build();
                // 读取sheet
                excelReader.read(readSheet);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                if (excelReader != null) {
                    //这里不能省略
                    excelReader.finish();
                }
            }
        }
    
    }
    
    

    还有注解 说明


    image.png

    这个没有测试


    image.png

    Hutool-poi 介绍

    Hutool-poi是针对Apache POI的封装,因此需要用户自行引入POI库,Hutool默认不引入。到目前为止,Hutool-poi支持:

    Excel文件(xls, xlsx)的读取(ExcelReader)
    Excel文件(xls,xlsx)的写出(ExcelWriter)

    主要是对poi 进行封装,也是一个不错的框架
    我的demo

    
        @GetMapping("/export")
        public void export(HttpServletResponse response) {
    //默认创建xls格式的(getWriter(true)则创建xlsx格式)
            ExcelWriter writer = ExcelUtil.getWriter(true);
    
            writer.addHeaderAlias("username", "姓名");
            writer.addHeaderAlias("age", "年龄");
            writer.addHeaderAlias("birthday", "生日");
    
            List<UserDTO> rows = getDataList();
    
            // 一次性写出内容,使用默认样式
            writer.write(rows, true);
            //response为HttpServletResponse对象
            response.setContentType("application/vnd.ms-excel");
            ServletOutputStream out = null;
            //文件名格式:文件+当前时间,如文件20220617094900
            String rawFileName = "文件" + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN);
            try {
                //弹出下载对话框的文件名不能为中文,中文请自行编码
                //P.S 这里可以中文命名,但是只能通过URL请求,通过swagger或postman请求还是会乱码,若要测试可以通过Get请求
                response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(rawFileName, "UTF-8") + ".xlsx");
                out = response.getOutputStream();
                writer.flush(out, true);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                // 关闭writer,释放内存
                writer.close();
                //此处记得关闭输出Servlet流
                IoUtil.close(out);
            }
        }
    
        public List getDataList() {
            List<UserDTO> list = new ArrayList<>();
            for (int i = 0; i < 100; i++) {
                UserDTO client = new UserDTO();
                client.setUsername("小明" + i);
                client.setBirthday("18797" + i);
                client.setAge("1" + i);
                list.add(client);
            }
            return list;
        }
    

    详细可以看 https://www.hutool.cn/docs/#/poi/%E6%A6%82%E8%BF%B0

    总结

    总上,都是api 的使用,看文档写demo 基本需求都能写出来的。整理和收集就到这里了。感觉
    easyexcel 好一点。不过其他都很优秀的。

    相关文章

      网友评论

          本文标题:java 导入导出Excel 坑位

          本文链接:https://www.haomeiwen.com/subject/omfewrtx.html