You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Web REST控制器如何接收DTO格式的CSV文件?

Spring Web 处理CSV转DTO的方案及错误分析

核心结论

Spring Web默认没有内置CSV到DTO的自动转换能力,没法像JSON那样直接通过@RequestBody绑定到自定义对象。你有两种选择:要么自定义消息转换器实现自动映射,要么手动接收文件后用Apache Commons CSV、OpenCSV这类外部库解析。

两种可行方案

方案1:自定义HttpMessageConverter实现自动转换

通过扩展Spring的消息转换器,让它支持text/csv类型的请求体自动转成DTO集合。以OpenCSV为例:

  1. 先引入依赖:
<dependency>
    <groupId>com.opencsv</groupId>
    <artifactId>opencsv</artifactId>
    <version>5.6</version>
</dependency>
  1. 实现自定义转换器:
@Component
public class CsvMessageConverter extends AbstractHttpMessageConverter<List<YourDto>> {

    private final ColumnPositionMappingStrategy<YourDto> mappingStrategy;

    public CsvMessageConverter() {
        super(MediaType.parseMediaType("text/csv"));
        mappingStrategy = new ColumnPositionMappingStrategy<>();
        mappingStrategy.setType(YourDto.class);
        // 按CSV列顺序对应DTO字段
        mappingStrategy.setColumnMapping("id", "name", "email");
    }

    @Override
    protected boolean supports(Class<?> clazz) {
        return List.class.isAssignableFrom(clazz) 
                && YourDto.class.isAssignableFrom(
                    ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0]
                );
    }

    @Override
    protected List<YourDto> readInternal(Class<? extends List<YourDto>> clazz, HttpInputMessage inputMessage) throws IOException {
        try (Reader reader = new InputStreamReader(inputMessage.getBody())) {
            CSVReader csvReader = new CSVReaderBuilder(reader).withSkipLines(1).build(); // 跳过表头
            return new CsvToBeanBuilder<YourDto>(csvReader)
                    .withMappingStrategy(mappingStrategy)
                    .build()
                    .parse();
        } catch (Exception e) {
            throw new HttpMessageNotReadableException("CSV解析失败", e, inputMessage);
        }
    }

    @Override
    protected void writeInternal(List<YourDto> yourDtos, HttpOutputMessage outputMessage) throws IOException {
        // 若需返回CSV则实现此方法,仅接收可忽略
    }
}
  1. 编写Controller接口:
@PostMapping(value = "/upload-csv", consumes = "text/csv")
public ResponseEntity<Void> uploadCsv(@RequestBody List<YourDto> dtoList) {
    // 处理DTO集合
    return ResponseEntity.ok().build();
}

请求时需将Content-Type设为text/csv,并把CSV文本直接放在请求体中。

方案2:手动接收文件解析(更常用、灵活)

直接通过MultipartFile接收上传的CSV文件,再用外部库解析成DTO:

@PostMapping("/upload-csv")
public ResponseEntity<String> uploadCsv(@RequestParam("file") MultipartFile file) {
    if (file.isEmpty()) {
        return ResponseEntity.badRequest().body("文件不能为空");
    }

    // 用Apache Commons CSV解析
    try (Reader reader = new InputStreamReader(file.getInputStream())) {
        CSVParser parser = CSVParser.parse(reader, CSVFormat.DEFAULT.withHeader().withSkipHeaderRecord());
        for (CSVRecord record : parser) {
            YourDto dto = new YourDto();
            dto.setId(Long.parseLong(record.get("id")));
            dto.setName(record.get("name"));
            dto.setEmail(record.get("email"));
            // 业务处理逻辑
        }
    } catch (Exception e) {
        return ResponseEntity.badRequest().body("CSV格式无效");
    }

    return ResponseEntity.ok("文件上传解析成功");
}

请求时需用multipart/form-data类型,将文件作为form参数上传(客户端会自动处理boundary,无需手动设置)。

你的错误原因分析

错误1:415不支持的媒体类型

  • 若你用@RequestBody接收CSV文本,但未设置Content-Type: text/csv,或Spring容器中没有对应的消息转换器,Spring无法识别请求格式,返回415。
  • 若你想上传文件却用了@RequestBody而非@RequestParam(MultipartFile),也会触发此错误。

错误2:缺少multipart boundary返回500,留空Content-Type返回415

  • 当使用multipart/form-data请求时,Content-Type必须包含boundary参数(用于分割请求体中的不同部分),手动设置时遗漏会导致服务器无法解析请求体,抛出500。
  • 留空Content-Type时,Spring无法识别请求类型,直接返回415不支持的媒体类型。

解决办法

  • 上传文件:用multipart/form-data请求,让客户端(如Postman、前端表单)自动生成boundary,无需手动设置Content-Type。
  • 传CSV文本:设置Content-Type: text/csv,配合自定义消息转换器用@RequestBody接收。

内容的提问来源于stack exchange,提问作者Cagepi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 10:42:16