上文,我们完成了简单的导入导出,但是是单体环境的读和写。实际成产环境一般都在web环境中,那我们这篇就结合SpringMVC实现文件上传和下载。请在保证熟悉SpringMVC相关知识后食用。

本文档相关资料及讲解视频见文末

3、文件上传和下载

基于SpringMVC的文件上传和下载

0. 导入依赖

<!-- EasyExcel -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.0.5</version>
</dependency>
<!-- lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.10</version>
    <scope>provided</scope>
</dependency>
<!-- junit -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-nop</artifactId>
    <version>1.7.2</version>
</dependency>
<!-- SpringMVC(Spring) -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>
<!-- Servlet -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
    <scope>provided</scope>
</dependency>
<!-- 文件上传 -->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>

3.1 文件上传

编写excel中每一行对应的实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
    /**
     * 学生姓名
     */
    private String name;
    /**
     * 学生性别
     */
    private String gender;

    /**
     * 学生出生日期
     */
    private Date birthday;
    /**
     * id
     */
    private String id;
}

编写回调监听器StudentReadListener

@Component
@Scope("prototype") // 作者要求每次读取都要使用新的Listener
public class StudentReadListener extends AnalysisEventListener<Student> {

    @Autowired
    private StudentService studentService;

    private final int BATCH_SAVE_NUM = 5;
    ArrayList<Student> students = new ArrayList<>();

    private int count = 0;

    // 每读一样,会调用该invoke方法一次
    @Override
    public void invoke(Student data, AnalysisContext context) {
        students.add(data);
        if (++count % BATCH_SAVE_NUM == 0) {
            studentService.save(students);
            students.clear();
        }
    }

    // 全部读完之后,会调用该方法
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // TODO......
    }
}

业务代码接口StudentService和实现类StudentServiceImpl

public interface StudentService {
    void save(ArrayList<Student> students);
}

@Service
public class StudentServiceImpl implements StudentService {
    @Override
    public void save(ArrayList<Student> students) {
        System.out.println("students in service = " + students);
    }
}

Spring配置文件

<!-- 组件扫描-->
<context:component-scan base-package="net.sunxiaowei"/>

SpringMVC配置文件

<!-- 组件扫描-->
<context:component-scan base-package="net.sunxiaowei.demo"/>

<!-- MVC文件上传多部件解析器 -->
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver"
      id="multipartResolver"/>

编码读取上传的Excel文件

@Controller
public class WebUploadAndDownload {
    /**
     * 文件上传
     * 1. 编写excel中每一行对应的实体类
     * 2. 由于默认异步读取excel,所以需要逐行读取的回调监听器
     * 3. 开始读取Excel
     */
    @PostMapping("upload")
    @ResponseBody
    public String upload(MultipartFile file) throws IOException {
        ExcelReaderBuilder workBook = EasyExcel.read(file.getInputStream(), Student.class, studentReadListener);
        workBook.sheet().doRead();
        return "success";
    }

3.2 文件下载

编写实体类并创建对象以便写入表格

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {

    /**
     * id
     */
    @ExcelIgnore
    private String id;
    /**
     * 学生姓名
     */
    //@ExcelProperty({"学员信息表", "学生姓名"})
    @ExcelProperty("学生姓名")
    private String name;
    /**
     * 学生性别
     */
    //@ExcelProperty({"学员信息表", "学生性别"})
    @ExcelProperty("学生性别")
    private String gender;

    /**
     * 学生出生日期
     */
    //@ExcelProperty({"学员信息表", "学生出生日期"})
    @ExcelProperty("学生出生日期")
    private Date birthday;
}

// 循环生成10个学生对象
private static List<Student> initData() {
    ArrayList<Student> students = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
        Student data = new Student();
        data.setName("模拟学号0" + i);
        data.setBirthday(new Date());
        data.setGender("男");
        students.add(data);
    }
    return students;
}

编码将数据写入到响应体实现下载

public class WebUploadAndDownload {

    /**
     * 文件下载
     * 1. 编写实体类并创建对象以便写入表格
     * 2. 设置响应参数:文件的ContentType和文件名,同时设置编码避免乱码
     * 3. 直接写,内部会调用finish方法自动关闭OutputStream
     */
    @GetMapping("download")
    public void download(HttpServletResponse response) throws IOException {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        // 防止中文乱码 
        String fileName = URLEncoder.encode("测试", "UTF-8");
        response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + fileName + ".xlsx");
        ExcelWriterBuilder workBook = EasyExcel.write(response.getOutputStream(), Student.class);

        ExcelWriterSheetBuilder sheet = workBook.sheet("模板");

        sheet.doWrite(initData());
    }
}

4、自定义单元格样式

EasyExcel支持调整行高、列宽、背景色、字体大小等内容,但是控制方式与使用原生POI无异,比较繁琐,不建议使用。

但是可以使用模板填充的方式,向预设样式的表格中直接写入数据,写入数据的时候会保持原有样式。

本文资料下载地址阿里云盘:EasyExcel资料下载
B站讲解视频:EasyExcel讲解视频
GitHub地址 官方文档地址

有帮到你吗?有用点一下哈|´・ω・)ノ