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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 16:37:44