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为例:
- 先引入依赖:
<dependency> <groupId>com.opencsv</groupId> <artifactId>opencsv</artifactId> <version>5.6</version> </dependency>
- 实现自定义转换器:
@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则实现此方法,仅接收可忽略 } }
- 编写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
相关产品推荐
相关产品推荐

