使用EasyExcel分页导出与导入大量数据


在我们使用easyexcel导出大量数据时,如果数据量过大会造成内存溢出,写了一个简单的demo提供参考。
提供了一下方法:

  1. 导入单个sheet数据
  2. 导入多个sheet数据(并提供了两个不同的实体类导入)
  3. 简单导出数据(适合少量数据)
  4. 分页导出数据
  5. 分页导出多sheet数据(Excel2003版最大行数是65536行,Excel2007开始的版本最大行数是1048576行)
  6. 分页导出多sheet数据并发送邮件(由于数据量过大后台运行时间过长,采用这个)

1.项目截图

在这里插入图片描述

2.项目使用的pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.easyexcel</groupId>
    <artifactId>easyexcel</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>easyexcel</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.2.6</version>
        </dependency>

        <!--配合插件lombok 使用-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.2</version>
            <scope>provided</scope>
        </dependency>

        <!--swagger依赖-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.16</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.27</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.1.0</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-extension</artifactId>
            <version>3.1.0</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.1.0</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-typehandlers-jsr310</artifactId>
            <version>1.0.2</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.71</version>
        </dependency>

        <!-- 分页插件  -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.3</version>
        </dependency>

        <!--发送邮件依赖jar包-->
        <!-- https://mvnrepository.com/artifact/javax.mail/javax.mail-api -->
        <dependency>
            <groupId>javax.mail</groupId>
            <artifactId>javax.mail-api</artifactId>
            <version>1.6.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.sun.mail/javax.mail -->
        <dependency>
            <groupId>com.sun.mail</groupId>
            <artifactId>javax.mail</artifactId>
            <version>1.6.1</version>
        </dependency>
        <!-- END: Java mail -->

    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
        </resources>
    </build>
    
</project>

3.配置文件

server:
  port: 8080
spring:
  datasource:
    druid:
      url: jdbc:mysql://localhost:3306/easyexcel?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
      username: root
      password: root
mybatis‐plus:
  typeAliasesPackage: com.easyexcel.dao
  mapper-locations: classpath*:com.easyexcel.mapper/*.xml

4.config配置类

4.1 MybatisPlusConfig

package com.easyexcel.config;

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * <P>
 * Mybatis-Plus 配置
 * </p>
 *
 * @author
 * @since 2020-12-23
 */
@Configuration
@MapperScan("com.easyexcel.mapper")
public class MybatisPlusConfig {

	/**
	 * 分页插件,自动识别数据库类型
	 */
	@Bean
	public PaginationInterceptor paginationInterceptor() {
		return new PaginationInterceptor();
	}

	/**
	 * 启用性能分析插件
	 */
	@Bean
	public PerformanceInterceptor performanceInterceptor(){
		return new PerformanceInterceptor();
	}
}

4.2 SwaggerConfiguration

package com.easyexcel.config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@ConditionalOnProperty(prefix = "swagger",value = {"enable"},havingValue = "true")
@EnableSwagger2
public class SwaggerConfiguration {

	@Bean
	public Docket buildDocket() {
		return new Docket(DocumentationType.SWAGGER_2)
				.apiInfo(buildApiInfo())
				.select()
				// 要扫描的API(Controller)基础包
				.apis(RequestHandlerSelectors.basePackage("com.easyexcel.controller"))
				.paths(PathSelectors.any())
				.build();
	}

	/**
	 * @param
	 * @return springfox.documentation.service.ApiInfo
	 * @Title: 构建API基本信息
	 * @methodName: buildApiInfo
	 */
	private ApiInfo buildApiInfo() {
		Contact contact = new Contact("开发者","","");
		return new ApiInfoBuilder()
				.title("API文档")
				.description("")
				.contact(contact)
				.version("1.0.0").build();
	}

}

5.工具类

5.1 DeleteFileUtil

package com.easyexcel.util;

import java.io.File;


public class DeleteFileUtil {
    /**
     * 删除文件,可以是文件或文件夹
     *
     * @param fileName 要删除的文件名
     * @return 删除成功返回true,否则返回false
     */
    public static boolean delete(String fileName) {
        File file = new File(fileName);
        if (!file.exists()) {
            return false;
        } else {
            if (file.isFile()) {
                return deleteFile(fileName);
            } else {
                return deleteDirectory(fileName);
            }
        }
    }

    /**
     * 删除单个文件
     *
     * @param fileName 要删除的文件的文件名
     * @return 单个文件删除成功返回true,否则返回false
     */
    public static boolean deleteFile(String fileName) {
        File file = new File(fileName);
        // 如果文件路径所对应的文件存在,并且是一个文件,则直接删除
        if (file.exists() && file.isFile()) {
            if (file.delete()) {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

    /**
     * 删除目录及目录下的文件
     *
     * @param dir 要删除的目录的文件路径
     * @return 目录删除成功返回true,否则返回false
     */
    public static boolean deleteDirectory(String dir) {
        // 如果dir不以文件分隔符结尾,自动添加文件分隔符
        if (!dir.endsWith(File.separator)) {
            dir = dir + File.separator;
        }
        File dirFile = new File(dir);
        // 如果dir对应的文件不存在,或者不是一个目录,则退出
        if ((!dirFile.exists()) || (!dirFile.isDirectory())) {
            return false;
        }
        boolean flag = true;
        // 删除文件夹中的所有文件包括子目录
        File[] files = dirFile.listFiles();
        for (int i = 0; i < files.length; i++) {
            // 删除子文件
            if (files[i].isFile()) {
                flag = DeleteFileUtil.deleteFile(files[i].getAbsolutePath());
                if (!flag) {
                    break;
                }
            }
            // 删除子目录
            else if (files[i].isDirectory()) {
                flag = DeleteFileUtil.deleteDirectory(files[i]
                        .getAbsolutePath());
                if (!flag) {
                    break;
                }
            }
        }
        if (!flag) {
            return false;
        }
        // 删除当前目录
        if (dirFile.delete()) {
            return true;
        } else {
            return false;
        }
    }
}

5.2 EasyExcelUtil

package com.easyexcel.util;


import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.util.CollectionUtils;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.List;


/**
 * @Copyright (C), 2018-2020
 * @FileName: EasyExcelUtil
 * @Author: 杜景涛
 * @Date: 2020/12/23 14:58
 * @Description: easyexcel工具类
 */
public class EasyExcelUtil {

    /**
     * 导出数据到excel
     *
     * @param response
     * @param fileName  文件名
     * @param sheetName 表格sheet名
     * @param list      导出数据
     * @param clazz     实体类
     */
    public static void export(HttpServletResponse response, String fileName, String sheetName, List<?> list, Class clazz) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        try (ServletOutputStream out = response.getOutputStream()) {
            response.setCharacterEncoding("utf8");
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName + ".xlsx", "UTF-8"));
            response.setHeader("Cache-Control", "no-store");
            response.addHeader("Cache-Control", "max-age=0");
            EasyExcel.write(out, clazz).sheet(sheetName).doWrite(list);
            out.flush();
        } catch (IOException e) {
            throw new RuntimeException();
        }
    }
}

5.3 EmailUtil

package com.easyexcel.util;

import java.util.Date;
import java.util.List;
import java.util.Properties;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.Authenticator;
import javax.mail.BodyPart;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;

/**
 * 发送邮件
 */
public class EmailUtil {
    private static final String SMTP_HOST = "smtp.163.com";
    private static final String USER_NAME = "dujingtao0228@163.com";
    //邮箱授权码
    private static final String PASSWORD = "*******";


    /**
     * 发送邮件
     *
     * @param title
     * @param content
     * @param receiveList
     * @throws Exception
     */
    public static void sendEmail(String title, String content, List<String> receiveList, String fileBytes) {


        Session session = EmailUtil.getSession();
        MimeMessage message = new MimeMessage(session);
        InternetAddress[] toArray = new InternetAddress[receiveList.size()];
        try {
            //接收列表
            for (int i = 0; i < toArray.length; i++) {
                toArray[i] = new InternetAddress(receiveList.get(i));
            }
            message.setSubject(title);
            message.setSentDate(new Date());
            message.setFrom(new InternetAddress(USER_NAME));
            message.addRecipients(MimeMessage.RecipientType.TO, toArray);
            // 创建消息部分
            BodyPart messageBodyPart = new MimeBodyPart();
            // 消息
            messageBodyPart.setContent(content, "text/html;charset=utf-8");
            // 创建多重消息
            Multipart multipart = new MimeMultipart();
            // 设置文本消息部分
            multipart.addBodyPart(messageBodyPart);
            // 附件部分
            messageBodyPart = new MimeBodyPart();
            // 设置要发送附件的文件路径
            DataSource source = new FileDataSource(fileBytes);
            messageBodyPart.setDataHandler(new DataHandler(source));
            // 处理附件名称中文(附带文件路径)乱码问题
            messageBodyPart.setFileName(MimeUtility.encodeText(title + ".zip"));
            multipart.addBodyPart(messageBodyPart);
            // 发送完整消息
            message.setContent(multipart);
            // 发送消息
            Transport.send(message);
        } catch (Exception e) {
        }
    }

    public static Session getSession() {
        Properties props = new Properties();
        props.setProperty("mail.transport.protocol", "smtp");
        props.setProperty("mail.smtp.host", SMTP_HOST);
        props.setProperty("mail.smtp.port", "25");
        props.setProperty("mail.smtp.auth", "true");
        Session session = Session.getInstance(props,
                new Authenticator() {
                    @Override
                    protected PasswordAuthentication getPasswordAuthentication() {
                        return new PasswordAuthentication(USER_NAME, PASSWORD);
                    }
                });

        return session;
    }

}

6.实体类

6.1 User

package com.easyexcel.dao;

import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;

/**
 * @Copyright (C), 2018-2020
 * @FileName: User
 * @Author: 杜景涛
 * @Date: 2020/12/23 14:47
 * @Description: 用户实体类
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("user")
public class User {
    /**
     * 用户id'
     */

    /**
     * 忽略这个字段
     */
    @ExcelIgnore
    @TableId(value = "id",type = IdType.AUTO)
    private Integer id;

    /**
     * 姓名
     */
    @ExcelProperty("姓名")
    private String name;

    /**
     * 性别
     */
    @ExcelProperty("性别")
    private String sex;

    /**
     * 年龄
     */
    @ExcelProperty("年龄")
    private Integer age;

    /**
     * 手机号
     */
    @ExcelProperty("手机号")
    private String phone;

    /**
     * 住址
     */
    @ExcelProperty("住址")
    private String address;

}

6.2 Achievement

package com.easyexcel.dao;

import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @Copyright (C), 2018-2020
 * @FileName: Achievement
 * @Author: 杜景涛
 * @Date: 2020/12/24 14:23
 * @Description: 成绩实体类
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("achievement")
public class Achievement {
    /**
     * id'
     */

    /**
     * 忽略这个字段
     */
    @ExcelIgnore
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    /**
     * 用户id
     */
    @ExcelProperty("用户id")
    @TableId(value = "uid")
    private Integer uid;
    /**
     * 语文
     */
    @ExcelProperty("语文")
    private Double chinese;
    /**
     * 数学
     */
    @ExcelProperty("数学")
    private Double mathematics;
    /**
     * 英语
     */
    @ExcelProperty("英语")
    private Double english;
    /**
     * 总和
     */
    @ExcelProperty("总和")
    private Double sum;

}

7. service接口

7.1 UserService

package com.easyexcel.service;

import com.easyexcel.dao.User;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * @Copyright (C), 2018-2020
 * @FileName: UserService
 * @Author: 杜景涛
 * @Date: 2020/12/23 14:57
 * @Description:
 */
public interface UserService {
    /**
     * 批量插入
     *
     * @param users
     */
    void insertBatch(List<User> users);

    /**
     * 导出数据
     *
     * @param response
     */
    void simpleWrite(HttpServletResponse response);

    /**
     * 分页导出数据
     *
     * @param response
     */
    void pageWrite(HttpServletResponse response);

    /**
     * 分页多sheet导出数据
     *
     * @param response
     */
    void pageWriteSheets(HttpServletResponse response);

    /**
     * 发送excel到邮箱
     *
     * @param response
     */
    void writeSendMail(HttpServletResponse response) throws IOException;
}

7.2 AchievementService

package com.easyexcel.service;

import com.easyexcel.dao.Achievement;

import java.util.List;

/**
 * @Copyright (C), 2018-2020
 * @FileName: AchievementService
 * @Author: 杜景涛
 * @Date: 2020/12/24 14:28
 * @Description:
 */
public interface AchievementService {
    /**
     * 批量插入
     *
     * @param achievements
     */
    void insertBatch(List<Achievement> achievements);
}

8. xml

8.1 UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.easyexcel.mapper.UserMapper">

    <insert id="insertBatch" parameterType="java.util.List">
        INSERT INTO user
        (name,sex,age,phone,address)
        VALUES
        <foreach collection="list" close="" index="index" item="item" open="" separator=",">
            (#{item.name},#{item.sex},#{item.age},#{item.phone},#{item.address})
        </foreach>
    </insert>
    <select id="findPageUser" resultType="com.easyexcel.dao.User">
        select * from user
    </select>
</mapper>

8.2 AchievementMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.easyexcel.mapper.AchievementMapper">

    <insert id="insertBatch" parameterType="java.util.List">
        INSERT INTO achievement
        (uid,chinese,mathematics,english,sum)
        VALUES
        <foreach collection="list" close="" index="index" item="item" open="" separator=",">
            (#{item.uid},#{item.chinese},#{item.mathematics},#{item.english},#{item.sum})
        </foreach>
    </insert>
</mapper>

9. service实现类

9.1 UserServiceImpl

package com.easyexcel.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.easyexcel.dao.User;
import com.easyexcel.mapper.UserMapper;
import com.easyexcel.service.UserService;
import com.easyexcel.util.DeleteFileUtil;
import com.easyexcel.util.EasyExcelUtil;
import com.easyexcel.util.EmailUtil;
import com.github.pagehelper.PageHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @Copyright (C), 2018-2020
 * @FileName: UserServiceImpl
 * @Author: 杜景涛
 * @Date: 2020/12/23 14:57
 * @Description:
 */
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    /**
     * 获得程序当前路径
     */
    private String basePath = System.getProperty("user.dir");

    @Override
    public void insertBatch(List<User> users) {
        userMapper.insertBatch(users);
    }

    @Override
    public void simpleWrite(HttpServletResponse response) {
        EasyExcelUtil.export(response, "test01", "sheet1", userMapper.findAll(), User.class);
    }

    @Override
    public void pageWrite(HttpServletResponse response) {
        ExcelWriter excelWriter = null;
        try (ServletOutputStream out = response.getOutputStream()) {
            //设置字符集为utf-8
            response.setCharacterEncoding("utf8");
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            //通知浏览器服务器发送的数据格式
            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode("Test02" + ".xlsx", "UTF-8"));
            //发送一个报头,告诉浏览器当前页面不进行缓存,每次访问的时间必须从服务器上读取最新的数据
            response.setHeader("Cache-Control", "no-store");
            response.addHeader("Cache-Control", "max-age=0");
            // 这里 需要指定写用哪个class去写
            excelWriter = EasyExcel.write(out, User.class).build();
            // 这里注意 如果同一个sheet只要创建一次
            WriteSheet writeSheet = EasyExcel.writerSheet("sheet").build();
            Integer number = userMapper.number();
            int page = (int) Math.ceil((double) number / (double) 10);
            // 去调用写入,根据数据库分页的总的页数来
            for (int i = 1; i <= page; i++) {
                //先定义一个空集合每次循环使他变成null减少内存的占用
                List<User> pageUser = null;
                PageHelper.startPage(i, 10);
                pageUser = userMapper.findPageUser();
                excelWriter.write(pageUser, writeSheet);
                pageUser.clear();
            }
            // 千万别忘记finish 会帮忙关闭流
            excelWriter.finish();
            out.flush();
        } catch (IOException e) {
            throw new RuntimeException();
        }
    }

    @Override
    public void pageWriteSheets(HttpServletResponse response) {
        ExcelWriter excelWriter = null;
        try (ServletOutputStream out = response.getOutputStream()) {
            response.setCharacterEncoding("utf8");
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode("Test03" + ".xlsx", "UTF-8"));
            response.setHeader("Cache-Control", "no-store");
            response.addHeader("Cache-Control", "max-age=0");
            // 这里 需要指定写用哪个class去写
            excelWriter = EasyExcel.write(out, User.class).build();
            // 这里注意 如果同一个sheet只要创建一次
            WriteSheet writeSheet = EasyExcel.writerSheet("sheet").build();
            Integer number = userMapper.number();
            Integer count = 0;
            int page = (int) Math.ceil((double) number / (double) 10);
            // 去调用写入,根据数据库分页的总的页数来
            for (int i = 1; i <= page; i++) {
                if (count == 2) {
                    writeSheet = EasyExcel.writerSheet("sheet2").build();
                    count = 0;
                }
                //先定义一个空集合每次循环使他变成null减少内存的占用
                List<User> pageUser = null;
                PageHelper.startPage(i, 10);
                pageUser = userMapper.findPageUser();
                excelWriter.write(pageUser, writeSheet);
                pageUser.clear();
                count++;
            }
            // 千万别忘记finish 会帮忙关闭流
            excelWriter.finish();
            out.flush();
        } catch (IOException e) {
            throw new RuntimeException();
        }
    }

    @Override
    public void writeSendMail(HttpServletResponse response) throws IOException {
        ExcelWriter excelWriter = null;
        UUID uuid = UUID.randomUUID();
        ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(basePath + "\\src\\main\\resources\\file\\" + uuid + ".zip"));
        ZipEntry entry = new ZipEntry("明细查询.xlsx");
        zip.putNextEntry(entry);
        try {
            // 这里需要指定写用哪个class去写
            excelWriter = EasyExcel.write(zip, User.class).build();
            // 这里注意 如果同一个sheet只要创建一次
            WriteSheet writeSheet = EasyExcel.writerSheet("sheet").build();
            Integer number = userMapper.number();
            int count = 0;
            int page = (int) Math.ceil((double) number / (double) 10);
            // 去调用写入,根据数据库分页的总的页数来
            for (int i = 1; i <= page; i++) {
                if (count == 2) {
                    writeSheet = EasyExcel.writerSheet("sheet2").build();
                    count = 0;
                }
                //先定义一个空集合每次循环使他变成null减少内存的占用
                List<User> pageUser = null;
                PageHelper.startPage(i, 10);
                pageUser = userMapper.findPageUser();
                excelWriter.write(pageUser, writeSheet);
                pageUser.clear();
                count++;
            }
        } finally {
            // 千万别忘记finish 会帮忙关闭流
            if (excelWriter != null) {
                excelWriter.finish();
                zip.flush();
                zip.close();
            }
        }
        List<String> receiveList = new ArrayList<>();
        receiveList.add("2209273361@qq.com");
        EmailUtil.sendEmail("明细查询", "查询数据已发送,请注意查收!", receiveList, basePath + "\\src\\main\\resources\\file\\" + uuid + ".zip");
        DeleteFileUtil.delete(basePath + "\\src\\main\\resources\\file\\" + uuid + ".zip");
    }


}

9.2 AchievementServiceImpl

package com.easyexcel.service.impl;

import com.easyexcel.dao.Achievement;
import com.easyexcel.mapper.AchievementMapper;
import com.easyexcel.service.AchievementService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @Copyright (C), 2018-2020
 * @FileName: AchievementServiceImpl
 * @Author: 杜景涛
 * @Date: 2020/12/24 14:29
 * @Description:
 */
@Service
public class AchievementServiceImpl implements AchievementService {
    @Autowired
    private AchievementMapper achievementMapper;

    @Override
    public void insertBatch(List<Achievement> achievements) {
        achievementMapper.insertBatch(achievements);
    }
}

10.拦截器

10.1 UserDataListener

package com.easyexcel.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSON;
import com.easyexcel.dao.User;
import com.easyexcel.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

/**
 * 模板的读取类
 */
public class UserDataListener extends AnalysisEventListener<User> {
    private static final Logger LOGGER = LoggerFactory.getLogger(UserDataListener.class);
    /**
     * 每隔3000条存储数据库,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 3000;
    List<User> list = new ArrayList<>();

    private UserService userService;


    /**
     * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
     *
     * @param userService
     */
    public UserDataListener(UserService userService) {
        this.userService = userService;
    }

    @Override
    public void invoke(User data, AnalysisContext context) {
        LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
        list.add(data);
        if (list.size() >= BATCH_COUNT) {
            saveData();
            list.clear();
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        saveData();
        LOGGER.info("所有数据解析完成!");
    }

    /**
     * 加上存储数据库
     */
    private void saveData() {
        LOGGER.info("{}条数据,开始存储数据库!", list.size());
        userService.insertBatch(list);
        LOGGER.info("存储数据库成功!");
    }
}

10.2 AchievementDataListener

package com.easyexcel.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSON;
import com.easyexcel.dao.Achievement;
import com.easyexcel.service.AchievementService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

/**
 * 模板的读取类
 */
public class AchievementDataListener extends AnalysisEventListener<Achievement> {
    private static final Logger LOGGER = LoggerFactory.getLogger(AchievementDataListener.class);

    /**
     * 每隔3000条存储数据库,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 3000;
    List<Achievement> list = new ArrayList<>();


    private AchievementService achievementService;


    /**
     * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
     *
     * @param achievementService
     */
    public AchievementDataListener(AchievementService achievementService) {
        this.achievementService = achievementService;
    }

    /**
     * 这个每一条数据解析都会来调用
     *
     * @param data
     * @param context
     */
    @Override
    public void invoke(Achievement data, AnalysisContext context) {
        LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
        list.add(data);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (list.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            list.clear();
        }
    }

    /**
     * 所有数据解析完成了 都会来调用
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        saveData();
        LOGGER.info("所有数据解析完成!");
    }

    /**
     * 加上存储数据库
     */
    private void saveData() {
        LOGGER.info("{}条数据,开始存储数据库!", list.size());
        achievementService.insertBatch(list);
        LOGGER.info("存储数据库成功!");
    }
}

11.Controller服务层

11.1UserController

package com.easyexcel.controller;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.easyexcel.dao.Achievement;
import com.easyexcel.dao.User;
import com.easyexcel.service.AchievementService;
import com.easyexcel.service.UserService;
import com.easyexcel.listener.UserDataListener;
import com.easyexcel.listener.AchievementDataListener;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;

/**
 * @Copyright (C), 2018-2020
 * @FileName: UserController
 * @Author: 杜景涛
 * @Date: 2020/12/23 14:58
 * @Description:
 */
@RestController
@RequestMapping("/v1")
@Api(tags = {"数据导出导入"})
public class UserController {

    @Resource
    private UserService userService;
    @Resource
    private AchievementService achievementService;

    @ApiOperation(value = "导入单个sheet数据")
    @PostMapping("/importSheet")
    public void importSheet(@RequestParam("multipartFile") MultipartFile multipartFile) {
        try {
            InputStream inputStream = multipartFile.getInputStream();
            //异步读取所有sheet数据,可在sheet方法参数中指定sheet索引,sheet名称
            EasyExcel.read(inputStream, User.class, new UserDataListener(userService)).sheet().doRead();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @ApiOperation(value = "导入多个sheet数据")
    @PostMapping("/importSheets")
    public void importSheets(@RequestParam("multipartFile") MultipartFile multipartFile) {
        ExcelReader excelReader = null;
        try {
            InputStream inputStream = multipartFile.getInputStream();
            excelReader = EasyExcel.read(inputStream).build();
            ReadSheet readSheet1 =
                    //构建ExcelReader对象
                    EasyExcel.readSheet(0).head(User.class).registerReadListener(new UserDataListener(userService)).build();
            ReadSheet readSheet2 =
                    //构建ExcelReader对象
                    EasyExcel.readSheet(1).head(Achievement.class).registerReadListener(new AchievementDataListener(achievementService)).build();
            //这里注意 一定要把sheet1 sheet2 一起传进去
            excelReader.read(readSheet1, readSheet2);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (excelReader != null) {
                // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
                excelReader.finish();
            }
        }
    }

    @ApiOperation(value = "导出数据", produces = "application/octet-stream")
    @PostMapping("/simpleWrite")
    public void simpleWrite(HttpServletResponse response) {
        userService.simpleWrite(response);
    }

    @ApiOperation(value = "分页导出数据", produces = "application/octet-stream")
    @PostMapping("/pageWrite")
    public void pageWrite(HttpServletResponse response) {
        userService.pageWrite(response);
    }

    @ApiOperation(value = "分页导出多sheet数据", produces = "application/octet-stream")
    @PostMapping("/pageWriteSheets")
    public void pageWriteSheets(HttpServletResponse response) {
        userService.pageWriteSheets(response);
    }

    @ApiOperation(value = "分页导出多sheet数据发送邮件")
    @PostMapping("/writeSendMail")
    public void writeSendMail(HttpServletResponse response) throws IOException {
        userService.writeSendMail(response);
    }
}

项目地址

https://gitee.com/du_jing_tao/easyexcel.git

EasyExcel 是一款针对 Excel 操作的开源 Java 类库,可以方便地实现 Excel 数据导入导出。下面是示例代码: 导出数据: 1. 首先需要定义对应的实体类,对应 Excel 中的每一列,例如: ```java @Data public class User { @ExcelProperty("用户ID") private Long id; @ExcelProperty("用户名") private String username; @ExcelProperty("性别") private String gender; // ... } ``` 2. 在需要导出数据的方法中使用 EasyExcel 的 API 进行导出操作: ```java public void exportUser(HttpServletResponse response) { List<User> userList = userService.listUsers(); try { EasyExcel.write(response.getOutputStream(), User.class).sheet("用户列表").doWrite(userList); } catch (IOException e) { e.printStackTrace(); } } ``` 导入数据: 1. 定义导入时需要的实体类,同样需要用 `@ExcelProperty` 注解来定义每个属性对应的列: ```java @Data public class UserImport { @ExcelProperty("用户ID") private Long id; @ExcelProperty("用户名") private String username; @ExcelProperty("性别") private String gender; // ... } ``` 2. 在需要导入数据的方法中使用 EasyExcel 的 `read()` 方法进行数据读取: ```java public void importUser(MultipartFile file) { try { List<UserImport> userList = EasyExcel.read(file.getInputStream(), UserImport.class, new ReadListener()).sheet().headRowNumber(1).doReadSync(); // 处理导入数据 } catch (IOException e) { e.printStackTrace(); } } ``` 以上就是使用 EasyExcel 进行数据导入导出的基本操作,需要注意的是,如果导出大量数据,建议采用分页导出的方式,避免内存溢出的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值