Spring Boot 3 文件上传下载与 Excel 导入导出完整指南

Spring Boot整合EasyExcel(完整版包含上传解析excel和下载模板) Spring Boot整合EasyExcel(完整版包含上传解析excel和下载模板)1. 加入依赖2. 对读取excel内容(批量添加)3. 模板下载: 1. 加入依赖 <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.2 阅读详情

Spring Boot 3 文件上传下载与 Excel 导入导出完整指南

1. 项目依赖配置

首先,我们需要在 pom.xml 中添加必要的依赖:

<?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 
         http://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>3.2.0</version>
        <relativePath/>
    </parent>
    
    <groupId>com.example</groupId>
    <artifactId>file-upload-download-demo</artifactId>
    <version>1.0.0</version>
    <name>file-upload-download-demo</name>
    
    <properties>
        <java.version>17</java.version>
        <hutool.version>5.8.22</hutool.version>
        <easyexcel.version>3.3.2</easyexcel.version>
    </properties>
    
    <dependencies>
        <!-- Spring Boot Web Starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <!-- Spring Boot Validation Starter (用于文件上传验证) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        
        <!-- Hutool 工具包 (简化文件操作) -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>${hutool.version}</version>
        </dependency>
        
        <!-- EasyExcel (处理Excel导入导出) -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>${easyexcel.version}</version>
        </dependency>
        
        <!-- Apache POI (EasyExcel底层依赖,通常不需要单独引入) -->
        <!-- 如果需要直接使用POI,可以引入以下依赖 -->
        <!--
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>5.2.4</version>
        </dependency>
        -->
        
        <!-- Spring Boot Test Starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

2. 应用配置文件

创建 application.yml 配置文件:

# application.yml
server:
  port: 8080

spring:
  servlet:
    multipart:
      # 启用文件上传
      enabled: true
      # 单个文件最大大小 (10MB)
      max-file-size: 10MB
      # 单次请求最大大小 (50MB)
      max-request-size: 50MB
      # 文件大小阈值,超过此值会写入磁盘
      file-size-threshold: 2KB
      # 临时文件位置
      location: ${java.io.tmpdir}
  
  # 文件存储配置
  file:
    upload:
      # 上传文件存储目录
      dir: ${user.home}/uploads
      # 允许的文件类型
      allowed-extensions: jpg,jpeg,png,gif,pdf,doc,docx,xls,xlsx,txt,csv
      # 最大文件大小 (字节)
      max-size: 10485760  # 10MB

# 日志配置
logging:
  level:
    com.example: debug

3. 公共响应封装类

创建统一的响应结果封装类:

package com.example.common;

import lombok.Data;
import lombok.experimental.Accessors;

/**
 * 统一响应结果封装类
 * @param <T> 响应数据类型
 */
@Data
@Accessors(chain = true)
public class Result<T> {
    /**
     * 响应状态码
     */
    private Integer code;
    
    /**
     * 响应消息
     */
    private String message;
    
    /**
     * 响应数据
     */
    private T data;
    
    /**
     * 成功响应构造方法
     */
    public static <T> Result<T> success() {
        return new Result<T>().setCode(200).setMessage("操作成功");
    }
    
    /**
     * 成功响应构造方法(带数据)
     */
    public static <T> Result<T> success(T data) {
        return new Result<T>().setCode(200).setMessage("操作成功").setData(data);
    }
    
    /**
     * 成功响应构造方法(带消息和数据)
     */
    public static <T> Result<T> success(String message, T data) {
        return new Result<T>().setCode(200).setMessage(message).setData(data);
    }
    
    /**
     * 失败响应构造方法
     */
    public static <T> Result<T> error(String message) {
        return new Result<T>().setCode(500).setMessage(message);
    }
    
    /**
     * 失败响应构造方法(带状态码和消息)
     */
    public static <T> Result<T> error(Integer code, String message) {
        return new Result<T>().setCode(code).setMessage(message);
    }
}

4. 文件上传下载控制器

4.1 基础文件上传下载控制器

package com.example.controller;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.example.common.Result;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 文件上传下载控制器
 * 提供基础的文件上传、下载、删除功能
 */
@Slf4j
@RestController
@RequestMapping("/api/file")
public class FileController {
    
    /**
     * 上传文件存储目录路径
     * 从配置文件中读取
     */
    @Value("${spring.file.upload.dir}")
    private String uploadDir;
    
    /**
     * 允许的文件扩展名列表
     * 从配置文件中读取并转换为List
     */
    @Value("${spring.file.upload.allowed-extensions}")
    private String allowedExtensions;
    
    /**
     * 最大文件大小(字节)
     * 从配置文件中读取
     */
    @Value("${spring.file.upload.max-size}")
    private long maxSize;
    
    /**
     * 初始化上传目录
     */
    @PostConstruct
    public void init() {
        // 创建上传目录(如果不存在)
        File uploadDirectory = new File(uploadDir);
        if (!uploadDirectory.exists()) {
            boolean created = uploadDirectory.mkdirs();
            if (created) {
                log.info("创建上传目录: {}", uploadDir);
            }
        }
        log.info("文件上传目录: {}", uploadDir);
    }
    
    /**
     * 单文件上传接口
     * 
     * @param file 上传的文件
     * @return 上传结果,包含文件访问路径
     */
    @PostMapping("/upload")
    public Result<String> uploadFile(@RequestParam("file") MultipartFile file) {
        try {
            // 验证文件是否为空
            if (file.isEmpty()) {
                return Result.error("上传文件不能为空");
            }
            
            // 验证文件大小
            if (file.getSize() > maxSize) {
                return Result.error("文件大小不能超过 " + (maxSize / 1024 / 1024) + "MB");
            }
            
            // 验证文件扩展名
            String originalFilename = file.getOriginalFilename();
            if (!isValidFileType(originalFilename)) {
                return Result.error("不支持的文件类型,请上传以下类型文件: " + allowedExtensions);
            }
            
            // 生成唯一的文件名(避免文件名冲突)
            String uniqueFilename = generateUniqueFilename(originalFilename);
            
            // 构建完整的文件路径
            Path filePath = Paths.get(uploadDir, uniqueFilename);
            
            // 保存文件到磁盘
            file.transferTo(filePath.toFile());
            
            // 返回文件访问路径(相对路径)
            String fileUrl = "/api/file/download/" + uniqueFilename;
            
            log.info("文件上传成功: 原始文件名={}, 保存路径={}", originalFilename, filePath);
            return Result.success("文件上传成功", fileUrl);
            
        } catch (IOException e) {
            log.error("文件上传失败", e);
            return Result.error("文件上传失败: " + e.getMessage());
        } catch (Exception e) {
            log.error("文件上传异常", e);
            return Result.error("文件上传异常: " + e.getMessage());
        }
    }
    
    /**
     * 多文件上传接口
     * 
     * @param files 上传的文件数组
     * @return 上传结果列表,包含每个文件的访问路径
     */
    @PostMapping("/upload/batch")
    public Result<List<String>> uploadFiles(@RequestParam("files") MultipartFile[] files) {
        if (files == null || files.length == 0) {
            return Result.error("上传文件不能为空");
        }
        
        // 限制一次上传的文件数量(可选)
        if (files.length > 10) {
            return Result.error("一次最多上传10个文件");
        }
        
        try {
            List<String> fileUrls = Arrays.stream(files)
                .filter(file -> !file.isEmpty()) // 过滤空文件
                .map(this::processSingleFile)    // 处理单个文件
                .filter(result -> result.getCode() == 200) // 过滤成功的结果
                .map(Result::getData)            // 提取文件URL
                .collect(Collectors.toList());
            
            if (fileUrls.isEmpty()) {
                return Result.error("没有有效的文件被上传");
            }
            
            log.info("批量文件上传成功,共上传 {} 个文件", fileUrls.size());
            return Result.success("批量文件上传成功", fileUrls);
            
        } catch (Exception e) {
            log.error("批量文件上传失败", e);
            return Result.error("批量文件上传失败: " + e.getMessage());
        }
    }
    
    /**
     * 处理单个文件上传的私有方法
     * 
     * @param file 单个文件
     * @return 上传结果
     */
    private Result<String> processSingleFile(MultipartFile file) {
        try {
            // 验证文件大小
            if (file.getSize() > maxSize) {
                return Result.error("文件 " + file.getOriginalFilename() + " 大小超过限制");
            }
            
            // 验证文件扩展名
            String originalFilename = file.getOriginalFilename();
            if (!isValidFileType(originalFilename)) {
                return Result.error("文件 " + originalFilename + " 类型不支持");
            }
            
            // 生成唯一文件名
            String uniqueFilename = generateUniqueFilename(originalFilename);
            Path filePath = Paths.get(uploadDir, uniqueFilename);
            
            // 保存文件
            file.transferTo(filePath.toFile());
            
            return Result.success("/api/file/download/" + uniqueFilename);
            
        } catch (IOException e) {
            log.error("处理文件 {} 失败", file.getOriginalFilename(), e);
            return Result.error("处理文件失败: " + e.getMessage());
        }
    }
    
    /**
     * 文件下载接口
     * 
     * @param filename 要下载的文件名
     * @param response HTTP响应对象
     * @throws IOException IO异常
     */
    @GetMapping("/download/{filename:.+}")
    public void downloadFile(@PathVariable String filename, HttpServletResponse response) throws IOException {
        // 验证文件名安全性(防止路径遍历攻击)
        if (!isValidFilename(filename)) {
            response.sendError(HttpStatus.BAD_REQUEST.value(), "无效的文件名");
            return;
        }
        
        // 构建文件完整路径
        Path filePath = Paths.get(uploadDir, filename);
        
        // 检查文件是否存在
        if (!Files.exists(filePath)) {
            response.sendError(HttpStatus.NOT_FOUND.value(), "文件不存在");
            return;
        }
        
        // 设置响应头
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        response.setContentLengthLong(Files.size(filePath));
        
        // 设置文件名(处理中文文件名)
        String encodedFilename = URLEncoder.encode(filename, StandardCharsets.UTF_8)
            .replaceAll("\\+", "%20"); // 处理空格
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, 
            "attachment; filename=\"" + encodedFilename + "\"; filename*=UTF-8''" + encodedFilename);
        
        // 写入文件内容到响应
        Files.copy(filePath, response.getOutputStream());
        response.getOutputStream().flush();
    }
    
    /**
     * 通过文件名直接下载(返回ResponseEntity方式)
     * 这种方式更适合需要返回JSON错误信息的场景
     * 
     * @param filename 要下载的文件名
     * @return 文件流响应
     */
    @GetMapping("/download2/{filename:.+}")
    public ResponseEntity<byte[]> downloadFile2(@PathVariable String filename) {
        try {
            // 验证文件名安全性
            if (!isValidFilename(filename)) {
                return ResponseEntity.badRequest().build();
            }
            
            // 构建文件路径
            Path filePath = Paths.get(uploadDir, filename);
            
            // 检查文件是否存在
            if (!Files.exists(filePath)) {
                return ResponseEntity.notFound().build();
            }
            
            // 读取文件内容
            byte[] fileContent = Files.readAllBytes(filePath);
            
            // 构建响应头
            String encodedFilename = URLEncoder.encode(filename, StandardCharsets.UTF_8)
                .replaceAll("\\+", "%20");
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.setContentLength(fileContent.length);
            headers.setContentDispositionFormData("attachment", encodedFilename);
            
            return ResponseEntity.ok()
                .headers(headers)
                .body(fileContent);
                
        } catch (IOException e) {
            log.error("文件下载失败: {}", filename, e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
    
    /**
     * 删除文件接口
     * 
     * @param filename 要删除的文件名
     * @return 删除结果
     */
    @DeleteMapping("/delete/{filename:.+}")
    public Result<String> deleteFile(@PathVariable String filename) {
        try {
            // 验证文件名安全性
            if (!isValidFilename(filename)) {
                return Result.error("无效的文件名");
            }
            
            // 构建文件路径
            Path filePath = Paths.get(uploadDir, filename);
            
            // 检查文件是否存在
            if (!Files.exists(filePath)) {
                return Result.error("文件不存在");
            }
            
            // 删除文件
            boolean deleted = Files.deleteIfExists(filePath);
            if (deleted) {
                log.info("文件删除成功: {}", filename);
                return Result.success("文件删除成功");
            } else {
                return Result.error("文件删除失败");
            }
            
        } catch (IOException e) {
            log.error("文件删除失败: {}", filename, e);
            return Result.error("文件删除失败: " + e.getMessage());
        }
    }
    
    /**
     * 验证文件扩展名是否合法
     * 
     * @param filename 文件名
     * @return 是否合法
     */
    private boolean isValidFileType(String filename) {
        if (!StringUtils.hasText(filename)) {
            return false;
        }
        
        // 获取文件扩展名(转换为小写)
        String extension = FileUtil.extName(filename).toLowerCase();
        
        // 检查扩展名是否在允许列表中
        List<String> allowedList = Arrays.asList(allowedExtensions.split(","));
        return allowedList.contains(extension);
    }
    
    /**
     * 验证文件名安全性(防止路径遍历攻击)
     * 
     * @param filename 文件名
     * @return 是否安全
     */
    private boolean isValidFilename(String filename) {
        if (!StringUtils.hasText(filename)) {
            return false;
        }
        
        // 检查是否包含路径分隔符(防止路径遍历)
        if (filename.contains("..") || filename.contains("/") || filename.contains("\\")) {
            return false;
        }
        
        // 检查文件名长度
        if (filename.length() > 255) {
            return false;
        }
        
        return true;
    }
    
    /**
     * 生成唯一的文件名
     * 使用UUID + 原始文件扩展名
     * 
     * @param originalFilename 原始文件名
     * @return 唯一文件名
     */
    private String generateUniqueFilename(String originalFilename) {
        // 获取文件扩展名
        String extension = FileUtil.extName(originalFilename);
        
        // 生成UUID(使用Hutool的IdUtil)
        String uuid = IdUtil.fastSimpleUUID();
        
        // 如果原始文件没有扩展名,则不添加扩展名
        if (StrUtil.isBlank(extension)) {
            return uuid;
        } else {
            return uuid + "." + extension;
        }
    }
}

5. Excel 数据模型

创建用于 Excel 导入导出的数据模型:

package com.example.model;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import lombok.Data;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * 用户数据模型
 * 用于Excel导入导出
 */
@Data
@HeadRowHeight(25)  // 表头行高
@ContentRowHeight(20) // 内容行高
@ColumnWidth(20)    // 默认列宽
public class UserExcelDTO {
    
    /**
     * 用户ID
     * Excel列名:用户ID
     * 列索引:0(第一列)
     */
    @ExcelProperty(value = "用户ID", index = 0)
    @ColumnWidth(15)
    private Long id;
    
    /**
     * 用户名
     * Excel列名:用户名
     */
    @ExcelProperty("用户名")
    @ColumnWidth(25)
    private String username;
    
    /**
     * 真实姓名
     * Excel列名:真实姓名
     */
    @ExcelProperty("真实姓名")
    @ColumnWidth(20)
    private String realName;
    
    /**
     * 邮箱
     * Excel列名:邮箱
     */
    @ExcelProperty("邮箱")
    @ColumnWidth(30)
    private String email;
    
    /**
     * 手机号
     * Excel列名:手机号
     */
    @ExcelProperty("手机号")
    @ColumnWidth(18)
    private String phone;
    
    /**
     * 年龄
     * Excel列名:年龄
     */
    @ExcelProperty("年龄")
    @ColumnWidth(10)
    private Integer age;
    
    /**
     * 工资
     * Excel列名:工资
     */
    @ExcelProperty("工资")
    @ColumnWidth(15)
    private BigDecimal salary;
    
    /**
     * 入职日期
     * Excel列名:入职日期
     * 日期格式:yyyy-MM-dd HH:mm:ss
     */
    @ExcelProperty("入职日期")
    @DateTimeFormat("yyyy-MM-dd HH:mm:ss")
    @ColumnWidth(22)
    private LocalDateTime hireDate;
    
    /**
     * 状态
     * Excel列名:状态
     * 0-禁用, 1-启用
     */
    @ExcelProperty("状态")
    @ColumnWidth(12)
    private Integer status;
}

6. Excel 导入导出控制器

package com.example.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.example.common.Result;
import com.example.model.UserExcelDTO;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * Excel导入导出控制器
 * 使用EasyExcel实现Excel文件的导入导出功能
 */
@Slf4j
@RestController
@RequestMapping("/api/excel")
public class ExcelController {
    
    /**
     * 模拟数据库数据(实际项目中应从数据库查询)
     */
    private List<UserExcelDTO> getMockUserData() {
        List<UserExcelDTO> users = new ArrayList<>();
        
        for (int i = 1; i <= 100; i++) {
            UserExcelDTO user = new UserExcelDTO();
            user.setId((long) i);
            user.setUsername("user" + i);
            user.setRealName("用户" + i);
            user.setEmail("user" + i + "@example.com");
            user.setPhone("1380013800" + (i % 10));
            user.setAge(20 + (i % 50));
            user.setSalary(java.math.BigDecimal.valueOf(5000 + (i * 100)));
            user.setHireDate(LocalDateTime.now().minusDays(i));
            user.setStatus(i % 2 == 0 ? 1 : 0);
            users.add(user);
        }
        
        return users;
    }
    
    /**
     * 导出Excel文件(全量导出)
     * 
     * @param response HTTP响应对象
     * @throws IOException IO异常
     */
    @GetMapping("/export")
    public void exportExcel(HttpServletResponse response) throws IOException {
        try {
            // 获取要导出的数据
            List<UserExcelDTO> userData = getMockUserData();
            
            // 设置响应头
            String fileName = "用户数据_" + DateUtil.format(DateUtil.date(), "yyyyMMdd_HHmmss") + ".xlsx";
            setExcelResponseHeader(response, fileName);
            
            // 使用EasyExcel写入数据
            EasyExcel.write(response.getOutputStream(), UserExcelDTO.class)
                .sheet("用户数据")
                .doWrite(userData);
                
            log.info("Excel导出成功,共导出 {} 条数据", userData.size());
            
        } catch (Exception e) {
            log.error("Excel导出失败", e);
            // 导出失败时返回错误信息
            response.reset();
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/json");
            response.getWriter().write("{\"code\":500,\"message\":\"导出失败: " + e.getMessage() + "\"}");
        }
    }
    
    /**
     * 分页导出Excel文件
     * 适用于大数据量导出,避免内存溢出
     * 
     * @param response HTTP响应对象
     * @throws IOException IO异常
     */
    @GetMapping("/export/paginated")
    public void exportExcelPaginated(HttpServletResponse response) throws IOException {
        try {
            // 设置响应头
            String fileName = "用户数据_分页_" + DateUtil.format(DateUtil.date(), "yyyyMMdd_HHmmss") + ".xlsx";
            setExcelResponseHeader(response, fileName);
            
            // 创建ExcelWriter
            ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), UserExcelDTO.class)
                .build();
            
            // 创建工作表
            WriteSheet writeSheet = EasyExcel.writerSheet("用户数据").build();
            
            // 分页查询并写入数据(模拟分页)
            int pageSize = 1000; // 每页1000条
            int currentPage = 0;
            List<UserExcelDTO> pageData;
            
            do {
                // 模拟分页查询(实际项目中应调用数据库分页查询)
                pageData = getMockPageData(currentPage, pageSize);
                
                if (!pageData.isEmpty()) {
                    // 写入当前页数据
                    excelWriter.write(pageData, writeSheet);
                    currentPage++;
                }
                
            } while (!pageData.isEmpty());
            
            // 关闭ExcelWriter
            excelWriter.finish();
            
            log.info("分页Excel导出成功,共导出 {} 页", currentPage);
            
        } catch (Exception e) {
            log.error("分页Excel导出失败", e);
            response.reset();
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/json");
            response.getWriter().write("{\"code\":500,\"message\":\"导出失败: " + e.getMessage() + "\"}");
        }
    }
    
    /**
     * 模拟分页数据查询
     * 
     * @param page 页码(从0开始)
     * @param size 每页大小
     * @return 分页数据
     */
    private List<UserExcelDTO> getMockPageData(int page, int size) {
        // 模拟总共10000条数据
        int totalRecords = 10000;
        int start = page * size;
        int end = Math.min(start + size, totalRecords);
        
        if (start >= totalRecords) {
            return CollUtil.newArrayList();
        }
        
        List<UserExcelDTO> pageData = new ArrayList<>();
        for (int i = start; i < end; i++) {
            UserExcelDTO user = new UserExcelDTO();
            user.setId((long) i + 1);
            user.setUsername("user" + (i + 1));
            user.setRealName("用户" + (i + 1));
            user.setEmail("user" + (i + 1) + "@example.com");
            user.setPhone("1380013800" + ((i + 1) % 10));
            user.setAge(20 + ((i + 1) % 50));
            user.setSalary(java.math.BigDecimal.valueOf(5000 + ((i + 1) * 100)));
            user.setHireDate(LocalDateTime.now().minusDays(i + 1));
            user.setStatus((i + 1) % 2 == 0 ? 1 : 0);
            pageData.add(user);
        }
        
        return pageData;
    }
    
    /**
     * Excel文件导入
     * 
     * @param file 上传的Excel文件
     * @return 导入结果
     */
    @PostMapping("/import")
    public Result<String> importExcel(@RequestParam("file") MultipartFile file) {
        try {
            // 验证文件
            if (file.isEmpty()) {
                return Result.error("请选择要导入的Excel文件");
            }
            
            String originalFilename = file.getOriginalFilename();
            if (!originalFilename.endsWith(".xlsx") && !originalFilename.endsWith(".xls")) {
                return Result.error("请上传Excel文件(.xlsx或.xls格式)");
            }
            
            // 读取Excel文件数据
            List<UserExcelDTO> importedData = new ArrayList<>();
            
            // 使用EasyExcel读取数据
            EasyExcel.read(file.getInputStream(), UserExcelDTO.class, 
                new com.alibaba.excel.read.listener.PageReadListener<UserExcelDTO>(dataList -> {
                    // 处理每一批数据(默认100条一批)
                    importedData.addAll(dataList);
                    
                    // 这里可以进行数据验证、转换、保存到数据库等操作
                    // 为了示例简单,这里只做简单处理
                    log.info("读取到 {} 条数据", dataList.size());
                }))
                .sheet()
                .doRead();
            
            // 验证导入的数据
            if (importedData.isEmpty()) {
                return Result.error("Excel文件中没有有效数据");
            }
            
            // 实际项目中,这里应该将数据保存到数据库
            // validateAndSaveData(importedData);
            
            log.info("Excel导入成功,共导入 {} 条数据", importedData.size());
            return Result.success("Excel导入成功,共导入 " + importedData.size() + " 条数据");
            
        } catch (Exception e) {
            log.error("Excel导入失败", e);
            return Result.error("Excel导入失败: " + e.getMessage());
        }
    }
    
    /**
     * 设置Excel文件下载的响应头
     * 
     * @param response HTTP响应对象
     * @param fileName 文件名
     * @throws IOException IO异常
     */
    private void setExcelResponseHeader(HttpServletResponse response, String fileName) throws IOException {
        // 设置内容类型
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        
        // 编码文件名(处理中文)
        String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8)
            .replaceAll("\\+", "%20");
        
        // 设置Content-Disposition头
        response.setHeader("Content-Disposition", 
            "attachment; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName);
    }
}

7. Excel 导入数据验证监听器

创建专门的数据验证监听器,用于处理导入过程中的数据验证:

package com.example.listener;

import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.example.model.UserExcelDTO;
import lombok.extern.slf4j.Slf4j;

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

/**
 * Excel导入数据验证监听器
 * 在读取Excel数据时进行数据验证
 */
@Slf4j
public class UserExcelDataListener implements ReadListener<UserExcelDTO> {
    
    /**
     * 每批处理的数据量
     */
    private static final int BATCH_COUNT = 100;
    
    /**
     * 临时存储数据的列表
     */
    private final List<UserExcelDTO> cachedData = new ArrayList<>();
    
    /**
     * 存储验证错误信息
     */
    private final List<String> errorMessages = new ArrayList<>();
    
    /**
     * 行号计数器(从1开始,包含表头)
     */
    private int currentRow = 1;
    
    @Override
    public void invoke(UserExcelDTO data, AnalysisContext context) {
        currentRow++;
        
        // 验证数据
        String validationError = validateData(data, currentRow);
        if (validationError != null) {
            errorMessages.add("第" + currentRow + "行: " + validationError);
            return; // 跳过无效数据
        }
        
        // 添加到缓存
        cachedData.add(data);
        
        // 达到批次数量时处理数据
        if (cachedData.size() >= BATCH_COUNT) {
            saveData();
            cachedData.clear();
        }
    }
    
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 处理剩余数据
        saveData();
        cachedData.clear();
        
        // 输出验证结果
        if (!errorMessages.isEmpty()) {
            log.warn("Excel导入完成,发现 {} 个验证错误", errorMessages.size());
            errorMessages.forEach(log::warn);
        } else {
            log.info("Excel导入完成,所有数据验证通过");
        }
    }
    
    /**
     * 保存数据到数据库(实际项目中实现)
     */
    private void saveData() {
        if (!cachedData.isEmpty()) {
            log.info("保存 {} 条数据到数据库", cachedData.size());
            // 实际项目中这里应该调用Service层保存数据
            // userService.saveBatch(cachedData);
        }
    }
    
    /**
     * 验证单行数据
     * 
     * @param data 要验证的数据
     * @param rowNumber 行号
     * @return 验证错误信息,null表示验证通过
     */
    private String validateData(UserExcelDTO data, int rowNumber) {
        // 验证用户名
        if (StrUtil.isBlank(data.getUsername())) {
            return "用户名不能为空";
        }
        if (data.getUsername().length() > 50) {
            return "用户名长度不能超过50个字符";
        }
        
        // 验证真实姓名
        if (StrUtil.isBlank(data.getRealName())) {
            return "真实姓名不能为空";
        }
        
        // 验证邮箱格式
        if (StrUtil.isNotBlank(data.getEmail())) {
            if (!data.getEmail().matches("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$")) {
                return "邮箱格式不正确";
            }
        }
        
        // 验证手机号格式
        if (StrUtil.isNotBlank(data.getPhone())) {
            if (!data.getPhone().matches("^1[3-9]\\d{9}$")) {
                return "手机号格式不正确";
            }
        }
        
        // 验证年龄范围
        if (data.getAge() != null) {
            if (data.getAge() < 0 || data.getAge() > 150) {
                return "年龄必须在0-150之间";
            }
        }
        
        // 验证工资
        if (data.getSalary() != null) {
            if (data.getSalary().compareTo(java.math.BigDecimal.ZERO) < 0) {
                return "工资不能为负数";
            }
        }
        
        // 验证状态
        if (data.getStatus() != null) {
            if (data.getStatus() != 0 && data.getStatus() != 1) {
                return "状态只能为0(禁用)或1(启用)";
            }
        }
        
        return null; // 验证通过
    }
    
    /**
     * 获取验证错误信息
     * 
     * @return 错误信息列表
     */
    public List<String> getErrorMessages() {
        return new ArrayList<>(errorMessages);
    }
    
    /**
     * 是否存在验证错误
     * 
     * @return true表示存在错误
     */
    public boolean hasErrors() {
        return !errorMessages.isEmpty();
    }
}

8. 使用验证监听器的导入控制器

package com.example.controller;

import com.alibaba.excel.EasyExcel;
import com.example.common.Result;
import com.example.listener.UserExcelDataListener;
import com.example.model.UserExcelDTO;
import lombok.extern.slf4j.Slf4j;
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;

/**
 * 使用验证监听器的Excel导入控制器
 */
@Slf4j
@RestController
@RequestMapping("/api/excel/validated")
public class ValidatedExcelController {
    
    /**
     * 使用验证监听器导入Excel
     * 
     * @param file 上传的Excel文件
     * @return 导入结果
     */
    @PostMapping("/import")
    public Result<String> importExcelWithValidation(@RequestParam("file") MultipartFile file) {
        try {
            // 验证文件
            if (file.isEmpty()) {
                return Result.error("请选择要导入的Excel文件");
            }
            
            String originalFilename = file.getOriginalFilename();
            if (!originalFilename.endsWith(".xlsx") && !originalFilename.endsWith(".xls")) {
                return Result.error("请上传Excel文件(.xlsx或.xls格式)");
            }
            
            // 创建验证监听器
            UserExcelDataListener listener = new UserExcelDataListener();
            
            // 读取Excel文件
            EasyExcel.read(file.getInputStream(), UserExcelDTO.class, listener)
                .sheet()
                .doRead();
            
            // 检查是否有验证错误
            if (listener.hasErrors()) {
                StringBuilder errorMessage = new StringBuilder("Excel导入完成,但存在以下问题:\n");
                listener.getErrorMessages().forEach(msg -> errorMessage.append(msg).append("\n"));
                return Result.error(errorMessage.toString());
            }
            
            return Result.success("Excel导入成功");
            
        } catch (Exception e) {
            log.error("Excel导入失败", e);
            return Result.error("Excel导入失败: " + e.getMessage());
        }
    }
}

9. 前端HTML测试页面

创建一个简单的HTML页面用于测试:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>文件上传下载测试</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        .container { max-width: 800px; margin: 0 auto; }
        .section { margin-bottom: 30px; padding: 20px; border: 1px solid #ddd; border-radius: 5px; }
        .button { padding: 10px 20px; margin: 5px; background: #007bff; color: white; border: none; border-radius: 3px; cursor: pointer; }
        .button:hover { background: #0056b3; }
        .result { margin-top: 10px; padding: 10px; background: #f8f9fa; border-radius: 3px; }
        input[type="file"] { margin: 10px 0; }
    </style>
</head>
<body>
    <div class="container">
        <h1>Spring Boot 3 文件上传下载与Excel导入导出测试</h1>
        
        <!-- 文件上传测试 -->
        <div class="section">
            <h2>1. 文件上传测试</h2>
            <input type="file" id="fileInput" multiple>
            <button class="button" onclick="uploadFile()">上传文件</button>
            <button class="button" onclick="uploadFiles()">批量上传</button>
            <div id="uploadResult" class="result"></div>
        </div>
        
        <!-- 文件下载测试 -->
        <div class="section">
            <h2>2. 文件下载测试</h2>
            <input type="text" id="filenameInput" placeholder="输入文件名(如:abc123.txt)">
            <button class="button" onclick="downloadFile()">下载文件</button>
            <div id="downloadResult" class="result"></div>
        </div>
        
        <!-- Excel导出测试 -->
        <div class="section">
            <h2>3. Excel导出测试</h2>
            <button class="button" onclick="exportExcel()">导出Excel</button>
            <button class="button" onclick="exportExcelPaginated()">分页导出Excel</button>
        </div>
        
        <!-- Excel导入测试 -->
        <div class="section">
            <h2>4. Excel导入测试</h2>
            <input type="file" id="excelFileInput" accept=".xlsx,.xls">
            <button class="button" onclick="importExcel()">导入Excel</button>
            <div id="importResult" class="result"></div>
        </div>
    </div>

    <script>
        // 基础URL
        const BASE_URL = 'http://localhost:8080/api';
        
        // 上传单个文件
        function uploadFile() {
            const fileInput = document.getElementById('fileInput');
            const file = fileInput.files[0];
            if (!file) {
                alert('请选择文件');
                return;
            }
            
            const formData = new FormData();
            formData.append('file', file);
            
            fetch(`${BASE_URL}/file/upload`, {
                method: 'POST',
                body: formData
            })
            .then(response => response.json())
            .then(data => {
                document.getElementById('uploadResult').innerHTML = 
                    `<strong>结果:</strong> ${JSON.stringify(data, null, 2)}`;
            })
            .catch(error => {
                console.error('上传失败:', error);
                document.getElementById('uploadResult').innerHTML = 
                    `<strong>错误:</strong> ${error.message}`;
            });
        }
        
        // 批量上传文件
        function uploadFiles() {
            const fileInput = document.getElementById('fileInput');
            const files = fileInput.files;
            if (files.length === 0) {
                alert('请选择文件');
                return;
            }
            
            const formData = new FormData();
            for (let i = 0; i < files.length; i++) {
                formData.append('files', files[i]);
            }
            
            fetch(`${BASE_URL}/file/upload/batch`, {
                method: 'POST',
                body: formData
            })
            .then(response => response.json())
            .then(data => {
                document.getElementById('uploadResult').innerHTML = 
                    `<strong>结果:</strong> ${JSON.stringify(data, null, 2)}`;
            })
            .catch(error => {
                console.error('批量上传失败:', error);
                document.getElementById('uploadResult').innerHTML = 
                    `<strong>错误:</strong> ${error.message}`;
            });
        }
        
        // 下载文件
        function downloadFile() {
            const filename = document.getElementById('filenameInput').value;
            if (!filename) {
                alert('请输入文件名');
                return;
            }
            
            window.location.href = `${BASE_URL}/file/download/${filename}`;
        }
        
        // 导出Excel
        function exportExcel() {
            window.location.href = `${BASE_URL}/excel/export`;
        }
        
        // 分页导出Excel
        function exportExcelPaginated() {
            window.location.href = `${BASE_URL}/excel/export/paginated`;
        }
        
        // 导入Excel
        function importExcel() {
            const fileInput = document.getElementById('excelFileInput');
            const file = fileInput.files[0];
            if (!file) {
                alert('请选择Excel文件');
                return;
            }
            
            const formData = new FormData();
            formData.append('file', file);
            
            fetch(`${BASE_URL}/excel/import`, {
                method: 'POST',
                body: formData
            })
            .then(response => response.json())
            .then(data => {
                document.getElementById('importResult').innerHTML = 
                    `<strong>结果:</strong> ${JSON.stringify(data, null, 2)}`;
            })
            .catch(error => {
                console.error('导入失败:', error);
                document.getElementById('importResult').innerHTML = 
                    `<strong>错误:</strong> ${error.message}`;
            });
        }
    </script>
</body>
</html>

10. 启动类

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * Spring Boot 应用启动类
 */
@SpringBootApplication
public class FileUploadDownloadApplication {
    public static void main(String[] args) {
        SpringApplication.run(FileUploadDownloadApplication.class, args);
    }
}

11. 最佳实践总结

11.1 文件上传最佳实践

  1. 安全性验证:

    • 验证文件扩展名
    • 验证文件大小
    • 防止路径遍历攻击
    • 验证文件内容类型(可选)
  2. 文件存储:

    • 使用唯一文件名避免冲突
    • 考虑使用云存储(如阿里云OSS、AWS S3)
    • 定期清理临时文件
  3. 性能优化:

    • 限制单次上传文件数量
    • 使用异步处理大文件
    • 考虑分片上传

11.2 文件下载最佳实践

  1. 响应头设置:

    • 正确设置Content-Type
    • 处理中文文件名编码
    • 设置合适的缓存策略
  2. 大文件处理:

    • 使用流式传输避免内存溢出
    • 支持断点续传(可选)

11.3 Excel导入导出最佳实践

  1. 导出优化:

    • 大数据量使用分页导出
    • 使用EasyExcel避免内存溢出
    • 添加进度提示(前端)
  2. 导入验证:

    • 使用监听器进行逐行验证
    • 提供详细的错误信息
    • 支持部分成功导入
  3. 用户体验:

    • 提供模板下载
    • 显示导入进度
    • 支持预览功能

11.4 异常处理

  1. 统一异常处理:

    @ControllerAdvice
    public class GlobalExceptionHandler {
        
        @ExceptionHandler(MultipartException.class)
        public ResponseEntity<Result<String>> handleMultipartException(MultipartException e) {
            return ResponseEntity.badRequest().body(Result.error("文件上传异常: " + e.getMessage()));
        }
        
        @ExceptionHandler(IOException.class)
        public ResponseEntity<Result<String>> handleIOException(IOException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(Result.error("IO异常: " + e.getMessage()));
        }
    }
    
  2. 日志记录:

    • 记录关键操作日志
    • 记录错误详细信息
    • 考虑记录操作用户信息

这个完整的实现涵盖了Spring Boot 3中文件上传下载和Excel导入导出的所有标准用法,包含了安全性、性能、用户体验等多个方面的最佳实践。

Spring Boot+poi实现excel文件上传、下载 Spring Boot+poi实现excel文件上传、下载 最近为了实现excel文件上传与下载找了很多的代码,可总有一些写的不是很完整而且用的方法也特别多(感受到了代码的强大),所以我决定综合资料自己做出来后也保存一份下来(亲测有效????) 1.poi介绍 Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功... 阅读详情

相关推荐

Springboot Excel 最简单的 多sheet表 导入导出

Springboot Excel 最简单的 多sheet表 导入导出

默默不代表沉默 4517

SpringBoot导出Excel的四种方式

第三种方案是通过excel模板来实现的方法,现在基本已经废弃了,之所以放在这里是为了做一个参照,让大家看看不同的实现方法的区别。这种方式通过创建一个Excel模板和对应模板的实体类来实现文件导出,优点是实体类里不需要加注解了,而且在excel里可以设计一些style和格式,缺点是配置excelTemplate太麻烦了。该方法的优点:不用excel模板,导出导出方便。本人力荐前两种方法,有大量的项目使用,相关的博客也比较多,尤其是easyexcel,官方对每种导入导出的方式都有完整的案例参考,简单上手!

1万+

【前后端分离系列】 Spring Boot + Vue 实现 EasyPOI 导入导出

前后端分离项目下,如何实现 Excel 的导入导出,一文彻底搞定!

wanghuichen的博客 4511

spring boot导入导出excel,集成EasyExcel

spring boot导入导出excel,集成EasyExcel

weixin_41463944的博客 1439

轴承寿命预测实战:手把手教你用IEEE PHM2012数据集训练模型(附Python代码)

本文详细介绍了如何利用IEEE PHM2012数据集构建轴承剩余寿命预测模型,从数据预处理、特征工程到LSTM模型训练与部署,提供了完整的Python代码实现。通过实战案例,帮助读者掌握预测性维护中的关键技术,提升工业设备的运行安全与效率。

weixin_29186249的博客 310

unity shader 后处理实现水墨风格渲染「Low Poly 」变「水墨画 」

水墨风格渲染 这次学校的比赛打算做一个中国古代背景的游戏,所以尝试做了水墨风格的渲染。 主要按以下四步来实现的效果: 根据色调和饱和度调整饱和度。 对图像进行模糊 水墨风格的物体边缘 物体内画笔笔触的模拟 接下来我分别就这四步依次展开说明: 调整饱和度 这里我使用Post-Processing Stack 的Color Grading 来调节饱和度,通过使用Grading Curv...

九级铁甲蛹的博客 1万+

SpringBoot 集成 EasyExcel 3.x 实现 Excel 导出

导出excel数据时,可能会遇到XXX状态、XXX类型和性别等需要转换的字段,例如:0->男,1->女。需实现Converter接口来自定义转换器。orElse(null)与orElseGet(null)区别:orElse(null)表示如果一个都没找到返回null。【orElse()中可以塞默认值。如果找不到就会返回orElse中你自己设置的默认值。orElseGet(null)表示如果一个都没找到返回null。【orElseGet()中可以塞默认值。

weixin_43522117的博客 2366

【SWAT水文模型】SWAT-CUP参数率定与模型验证实战指南

本文提供了SWAT水文模型参数率定与模型验证的完整实战指南。详细介绍了如何使用SWAT-CUP工具,特别是SUFI-2算法,进行自动化参数校准,并阐述了模型验证的关键步骤以避免过度拟合。内容涵盖从工程搭建、核心参数选择、迭代优化到结果分析与常见问题排查的全流程,旨在帮助用户提升SWAT模型模拟的准确性与可靠性。

weixin_29072299的博客 498

Spring Boot 3 中实现文件上传、下载以及 Excel 导入导出

Spring Boot 3 文件操作指南:使用 Apache POI 和 Spring Web MVC 实现文件上传下载及 Excel 导入导出。通过 SpringDoc OpenAPI 3 提供 API 文档支持,包含生产级配置和详细注释。关键实现包括:Maven 依赖配置(Spring Web、Apache POI、SpringDoc)、文件大小限制设置、文件控制器(上传/下载逻辑)以及 OpenAPI 注解规范。支持.xlsx格式处理,采用UUID生成唯一文件名确保安全,提供完整的异常处理和响应封装。

python15397的博客 1257

XianYuAutoDeliveryX:基于闲鱼API与大模型的智能自动发货系统实践

本文详细介绍了如何利用XianYuAutoDeliveryX系统,结合闲鱼API与大语言模型,构建一个智能自动发货解决方案。该系统能实现买家付款后秒级自动发货,并通过对接ChatGPT等大模型智能回复买家咨询,有效解放卖家时间,提升虚拟商品店铺的运营效率和客户体验。

bush的博客 1012

spdk + fio测试nvme 设备的性能

fio的测试对象可以是块设备、文件等,在spdk的使用过程中会unbind默认的nvme driver,所以在系统中是看不到nvme块设备的,在spdk中可以通过fio_plugin的方式,将spdk的用户态driver部分的io功能打包成一个ioengine提供给fio使用,可以综合spdk的高性能和fio提供的复杂场景。更多DPDK相关学习资料有需要的可以自行报名学习,免费订阅,永久学习,或点击这里加。这个jobfile是spdk中提供的例子。领取,关注我持续更新哦!2.7 jobfile的例子。

lingshengxiyou的博客 1088

Spring Boot中Excel处理完全指南

在企业应用开发中,Excel文件处理是一个非常常见的需求,主要用于以下场景:Java中处理Excel文件的主要库有以下几种:Apache POI是Java中使用最广泛的Excel处理库,提供了全面的API来创建、读取和修改Office文档。优点:缺点:EasyExcel是阿里巴巴开源的Excel处理库,基于POI,但做了大量优化。优点:缺点:JExcel是另一个处理Excel的Java库。优点:缺点:SXSSF是POI提供的一种流式处理模式,专为处理大型Excel文件设计。优点:缺点:Spring Boot

yk_dflkg的博客 2111

超十万字解读Spring AI实战:开篇

大模型技术正以惊人的速度改变世界,2025年的人工智能已不再是科幻,而是现实。大模型如ChatGPT等,正在重塑各行各业,降低技术门槛,使专业能力普及化。SpringAI的出现为Java开发者提供了桥梁,使其能在企业级系统中稳定运行AI,同时保持工程化思维。尽管SpringAI在生态、学习成本和性能上存在不足,但其企业级基因和国产化适配能力使其成为Java开发者的重要工具。本系列文章旨在介绍SpringAI的功能与特性,鼓励更多Java开发者参与AI实践,共同探索AI时代的无限可能。

liaokailin的专栏 1575

Excel 导入导出spring boot starter 组件 业务代码零侵入

Excel 导入导出spring boot starter 组件 业务代码零侵入。

qq_20466509的博客 765

2026华为OD面试题029:勇攀数字高峰

在给定的数字地形图里寻找登山路径,数字代表当前位置的海拔高度。要求从最低海拔出发,不断攀登,最终到达最高山峰。地图保证最低海拔和最高山峰都只有一个。 登山规则: - 路径上的海拔必须严格递增 - 只能向上下左右 4 个方向移动 - 路径必须从最低海拔开始,到最高海拔结束 - 每个地点只能走一次 - 每一步的攀登高度差必须大于 0,且不超过指定值 `K` 输出满足条件的登山路径的数量。

琅嬛福地 112

2026华为OD机试真题 9月27日 新系统 【均衡调度】

华为OD机试真题 新系统 9月27号【均衡调度】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD机试真题 【均衡调度】多语言题解。

qq_45776114的博客 1759

Spring Boot 文件上传与下载(图片 _ Excel)

本文承接纯接口开发的基础,完整实战了Spring Boot文件操作的核心场景——本地文件上传、图片上传与回显、Excel上传解析(POI+EasyExcel),覆盖小工具开发的核心需求,核心要点:本地文件上传:核心是生成唯一文件名、创建存储目录、校验文件合法性,通用适配所有文件类型。图片上传:额外增加格式校验、图片压缩,通过静态资源配置实现图片回显,贴合实际开发场景。Excel解析:POI适合复杂场景,EasyExcel适合大数据量,按需选择,核心是将Excel数据映射为实体类,便于后续业务处理。

码客日记的博客 761

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

Spring Boot中Excel数据导入导出的高效实现

在企业级应用中,Excel文件的导入导出是一个常见的需求。本文将介绍如何在Spring Boot项目中使用EasyExcel库实现Excel文件的导入导出功能。我们将通过实际的代码示例,展示如何读取和写入Excel文件,以及如何通过自定义监听器来增强数据处理的灵活性。

interest_ing_的博客 3433

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

EasyExcel与Spring Boot集成:打造高效Excel导入导出接口

本文所有示例代码已上传至代码仓库: `https://gitcode.com/gh_mirrors/ea/easyexcel` 包含以下核心模块: - `excel-demo`: 基础导入导出示例 - `excel-advanced`: 自定义转换器和模板导出 - `excel-async`: 异步导出架构实现 - `excel-validator`: 导入数据校验框架 可通过以下命令快速...

gitblog_00278的博客 736

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

Spring Boot + Apache POI 实现 Excel 导入导出

Autowired/*** Excel 导入接口* 接收参数:customerId(客户ID)+ file(Excel 文件)*/@ApiOperation(value = "已购设备清单导入")try {return ApiResult.success("导入成功", result);关键点解析:接收上传的 Excel 文件:业务参数(客户ID)使用try-catch捕获异常,返回友好的错误信息@Autowired/*** Excel 导出接口。

qq_68599880的博客 1120

Spring Boot EasyPoi导出Excel下载

Java关于excel的操作 Java Excel俗称jxl,可以读取Excel文件的内容、创建新的Excel文件、更新已经存在的Excel文件,现在基本没有更新了 Apache POI是Apache基金组织Jakarta项目的子项目,它包括一系列的API,可以操作多种格式的Microsoft Office文件,通过这些API使Java更方便的操作Excel、Word等格式的Office文件 Ea...

weixin_45743799的博客 1911

使用springmvc来实现Excel文件导入导出

当前B/S模式已成为应用开发的主流,而在企业办公系统中,常常有客户这样子要求:你要把我们的报表直接用Excel打开(电信系统、银行系统)。或者是:我们已经习惯用Excel打印。这样在我们实际的开发中,很多时候需要实现导入、导出Excel的应用。目前,比较常用的实现Java导入、导出Excel的技术有两种Jakarta POI和Java Excel下面我就分别讲解一下在springMvc(Springboot)中如何使用这两个技术实现导入、导出ExcelJakarta POI 是一套用于访问微软格式文档的Ja

苍煜 4319

Spring Boot + EasyExcel 实现 Excel 导入导出(实战篇)

*** EasyExcel 导入监听器(处理Excel解析后的每一行数据)* 说明:监听器不能被Spring管理,需手动new,因此通过构造方法传入Service// 批量入库阈值(每100条数据批量插入一次,可调整) private static final int BATCH_SIZE = 100;// 存储解析后的Excel数据 private List < ExcelUserDTO > userList = new ArrayList < >();

码客日记的博客 1100

Spring Boot使用EasyExcel导入导出Excel

Spring Boot使用EasyExcel导入导出Excel

吴名氏的博客 14万+

Spring Boot框架下实现Excel服务端导入导出

比如SDK版本是11,语法选择8。考虑到导入导出功能需要对Excel具有较高的还原度,这里使用了SpreadJS组件,通过SpreadJS组件的ExcelIO功能,进行Excel的导入与导出。将服务端的文件以文件流的形式传输至前端,前端通过ExcelIO将结果导入结果呈现值SpreadJS中,所以导入的传递的参数是一个文件路径。ArtifactId:是项目的唯一标识符,在实际开发中一般对应项目的名称,就是项目根目录的名称。由于前端传入的是一个文件的路径,所以参数这里我们需要一个字符串类型的参数去接收。

葡萄城技术团队博客 2612
上一篇: Spring Boot 3 中实现文件上传、下载以及 Excel 导入导出
下一篇: Hutool 基本介绍和使用说明
龙茶清欢
博客等级 码龄7年 3720粉丝 375原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙茶清欢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值