如何在Spring Boot后端获取Angular上传文件的checksum、author属性
问题描述
现有一段可正常接收Angular前端上传MultipartFile的Spring Boot接口代码,需求是获取每个上传文件附带的checksum(校验和)和author(上传者)属性。
原接口代码:
@PostMapping(value = "/upload") @Operation(summary = "Upload Files") public ResponseEntity<Map<String, List<String>>> uploadFiles(@RequestParam("files") MultipartFile[] files) { List<String> exceptions = new ArrayList<>(); List<String> fileNames = new ArrayList<>(); for (MultipartFile file : files) { try { Path fileStorage = get(DIRECTORY, fileName).toAbsolutePath().normalize(); copy(file.getInputStream(), fileStorage, true); fileNames.add(fileName); } catch (Exception e) { exceptions.add(e.getClass().getName() + ": " + e.getMessage()); } } Map<String, List<String>> map = new HashMap<>(); map.put("data", fileNames); map.put("errors", exceptions); return ResponseEntity.ok().body(map); }
解决方案
根据前端FormData的构造逻辑,后端有两种常见实现方式:
方式一:按索引匹配文件与附加属性
如果前端为每个文件单独传递对应索引的checksum和author参数(如checksums数组、authors数组,与files数组索引一一对应),可通过@RequestParam接收数组参数后按索引匹配。
修改后的接口代码:
@PostMapping(value = "/upload") @Operation(summary = "Upload Files") public ResponseEntity<Map<String, Object>> uploadFiles( @RequestParam("files") MultipartFile[] files, @RequestParam("checksums") String[] checksums, @RequestParam("authors") String[] authors) { List<String> exceptions = new ArrayList<>(); List<Map<String, String>> fileResults = new ArrayList<>(); // 校验参数数量匹配性 if (checksums.length != files.length || authors.length != files.length) { exceptions.add("文件数量与附加属性数量不匹配"); Map<String, Object> errorMap = new HashMap<>(); errorMap.put("data", fileResults); errorMap.put("errors", exceptions); return ResponseEntity.badRequest().body(errorMap); } for (int i = 0; i < files.length; i++) { MultipartFile file = files[i]; String checksum = checksums[i]; String author = authors[i]; try { String fileName = file.getOriginalFilename(); // 修正路径处理逻辑,避免安全风险 Path fileStorage = Paths.get(DIRECTORY, fileName).toAbsolutePath().normalize(); Files.copy(file.getInputStream(), fileStorage, StandardCopyOption.REPLACE_EXISTING); Map<String, String> result = new HashMap<>(); result.put("fileName", fileName); result.put("checksum", checksum); result.put("author", author); fileResults.add(result); } catch (Exception e) { exceptions.add(String.format("文件[%s]上传失败: %s: %s", file.getOriginalFilename(), e.getClass().getName(), e.getMessage())); } } Map<String, Object> map = new HashMap<>(); map.put("data", fileResults); map.put("errors", exceptions); return ResponseEntity.ok().body(map); }
前端FormData构造示例(Angular):
const formData = new FormData(); files.forEach((file, index) => { formData.append('files', file); formData.append('checksums', file.checksum); formData.append('authors', file.author); });
方式二:封装DTO对象统一接收
如果前端将每个文件及其附加属性封装为独立的请求部分,可定义DTO类,通过@RequestPart接收列表形式的DTO对象。
步骤1:定义文件上传DTO
public class FileUploadDTO { private MultipartFile file; private String checksum; private String author; // Getter & Setter public MultipartFile getFile() { return file; } public void setFile(MultipartFile file) { this.file = file; } public String getChecksum() { return checksum; } public void setChecksum(String checksum) { this.checksum = checksum; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } }
步骤2:修改接口代码
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @Operation(summary = "Upload Files") public ResponseEntity<Map<String, Object>> uploadFiles( @RequestPart("fileDtos") List<FileUploadDTO> fileDtos) { List<String> exceptions = new ArrayList<>(); List<Map<String, String>> fileResults = new ArrayList<>(); for (FileUploadDTO dto : fileDtos) { MultipartFile file = dto.getFile(); String checksum = dto.getChecksum(); String author = dto.getAuthor(); try { String fileName = file.getOriginalFilename(); Path fileStorage = Paths.get(DIRECTORY, fileName).toAbsolutePath().normalize(); Files.copy(file.getInputStream(), fileStorage, StandardCopyOption.REPLACE_EXISTING); Map<String, String> result = new HashMap<>(); result.put("fileName", fileName); result.put("checksum", checksum); result.put("author", author); fileResults.add(result); } catch (Exception e) { exceptions.add(String.format("文件[%s]上传失败: %s: %s", file.getOriginalFilename(), e.getClass().getName(), e.getMessage())); } } Map<String, Object> map = new HashMap<>(); map.put("data", fileResults); map.put("errors", exceptions); return ResponseEntity.ok().body(map); }
前端FormData构造示例(Angular)
const formData = new FormData(); files.forEach((file) => { // 将非文件属性转为JSON Blob,指定媒体类型 const metaBlob = new Blob( [JSON.stringify({ checksum: file.checksum, author: file.author })], { type: 'application/json' } ); formData.append('fileDtos', file, file.name); formData.append('fileDtos', metaBlob); });
注意事项
- 前端传递的参数名必须与后端
@RequestParam/@RequestPart指定的名称完全一致。 - 路径处理需使用
Paths.get避免路径遍历安全风险,原代码中的get(DIRECTORY, fileName)需替换为标准写法。 - 必须添加参数数量校验,防止因数组长度不匹配导致索引越界异常。
内容的提问来源于stack exchange,提问作者ericute
相关产品推荐
相关产品推荐

