Spring Boot 多文件上传功能实现

1,396 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第21天,点击查看活动详情

文件名相同时的多文件上传处理

编写新页面,代码如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Spring Boot 多文件上传测试(file name 相同)</title>
</head>
<body>
<form action="/uploadFilesBySameName" method="post" enctype="multipart/form-data">
    <input type="file" name="files"/><br><br>
    <input type="file" name="files"/><br><br>
    <input type="file" name="files"/><br><br>
    <input type="file" name="files"/><br><br>
    <input type="file" name="files"/><br><br>
    <input type="submit" value="文件上传"/>
</form>
</body>
</html>

image.png

后端处理文件上传的请求地址为/uploadFilesBySameName,请求方法为POST

@RequestMapping(value = "/uploadFilesBySameName", method = RequestMethod.POST)
@ResponseBody
public String uploadFilesBySameName(@RequestPart MultipartFile[] files) {
    if (files == null || files.length == 0) {
        return "参数异常";
    }
    if (files.length > 5) {
        return "最多上传5个文件";
    }
    String uploadResult = "上传成功,地址为:<br>";
    for (MultipartFile file : files) {
        String fileName = file.getOriginalFilename();
        if (StringUtils.isEmpty(fileName)) {
            //表示无文件信息,跳出当前循环
            continue;
        }
        String suffixName = fileName.substring(fileName.lastIndexOf("."));
        //生成文件名称通用方法
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss");
        Random r = new Random();
        StringBuilder tempName = new StringBuilder();
        tempName.append(sdf.format(new Date())).append(r.nextInt(100)).append(suffixName);
        String newFileName = tempName.toString();
        try {
            // 保存文件
            byte[] bytes = file.getBytes();
            Path path = Paths.get(FILE_UPLOAD_PATH + newFileName);
            Files.write(path, bytes);
            uploadResult += "/upload/" + newFileName + "<br>";
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return uploadResult;
}

第一,文件参数在接收时的代码改动。在多文件上传并接收参数时使用的是@RequestPart注解,且接收的文件参数是一个数组MultipartFile。而单文件在上传时使用的是@RequestParam注解,接收的文件是单个对象。

第二,文件在保存时增加循环逻辑。多文件保存的处理方式与单文件在上传时比较类似,只是增加了循环逻辑,对接收的MultipartFile数组中每一个文件进行存储操作,最后拼接文件的地址信息并返回。

另外一个,多文件上传在接收参数时,参数名称files需要完全对应input框中的name属性。在upload-same-html文件中所有文件输入框的name属性都是files;在后端处理时,uploadFilesBySameName()方法的参数名称也定义为files,两个名称是对应的。如果所有文件输入框的name属性都改为uploadFiles,那么uploadFilesBySameName()方法的参数名称也需要改为uploadFiles,否则接收的文件对象数组为空。

文件名不同时的多文件上传处理

编写新页面,代码如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Spring Boot 多文件上传测试(file name 不同)</title>
</head>
<body>
<form action="/uploadFilesByDifferentName" method="post" enctype="multipart/form-data">
    <input type="file" name="file1"/><br><br>
    <input type="file" name="file2"/><br><br>
    <input type="file" name="file3"/><br><br>
    <input type="file" name="file4"/><br><br>
    <input type="file" name="file5"/><br><br>
    <input type="submit" value="文件上传"/>
</form>
</body>
</html>
@RequestMapping(value = "/uploadFilesByDifferentName", method = RequestMethod.POST)
@ResponseBody
public String uploadFilesByDifferentName(HttpServletRequest httpServletRequest) {
    List<MultipartFile> multipartFiles = new ArrayList<>(8);
    // 如果不是文件上传请求则不处理
    if (!standardServletMultipartResolver.isMultipart(httpServletRequest)) {
        return "请选择文件";
    }
    // 将 HttpServletRequest 对象转换为 MultipartHttpServletRequest 对象,之后读取文件
    MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) httpServletRequest;
    Iterator<String> iter = multiRequest.getFileNames();
    int total = 0;
    while (iter.hasNext()) {
        if (total > 5) {
            return "最多上传5个文件";
        }
        total += 1;
        MultipartFile file = multiRequest.getFile(iter.next());
        multipartFiles.add(file);
    }
    if (CollectionUtils.isEmpty(multipartFiles)) {
        return "请选择文件";
    }
    if (multipartFiles != null && multipartFiles.size() > 5) {
        return "最多上传5个文件";
    }
    String uploadResult = "上传成功,地址为:<br>";
    for (int i = 0; i < multipartFiles.size(); i++) {
        String fileName = multipartFiles.get(i).getOriginalFilename();
        if (StringUtils.isEmpty(fileName)) {
            //表示无文件信息,跳出当前循环
            continue;
        }
        String suffixName = fileName.substring(fileName.lastIndexOf("."));
        //生成文件名称通用方法
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss");
        Random r = new Random();
        StringBuilder tempName = new StringBuilder();
        tempName.append(sdf.format(new Date())).append(r.nextInt(100)).append(suffixName);
        String newFileName = tempName.toString();
        try {
            // 保存文件
            byte[] bytes = multipartFiles.get(i).getBytes();
            Path path = Paths.get(FILE_UPLOAD_PATH + newFileName);
            Files.write(path, bytes);
            uploadResult += "/upload/" + newFileName + "<br>";
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return uploadResult;
}

与文件名相同时的多文件上传的uploadFilesBySameName()方法相比,文件名不容时的改动只有一处,即文件参数在接收时的代码做了改动。在读取文件信息时的逻辑是自行实现的代码逻辑,

首先调用isMultipart()方法判断当前请求是否为文件上传请求,如果不是则不进行处理,如果是文件上传请求,则HttpServletReques对象转换为MultipartHttpServletRequest对象,然后读取文件数据,在读取完成后再依次进行存储。存储文件的过程与之前的逻辑一致,最后拼接文件的地址信息并返回。