Hutool 中转 MultipartFile 文件上传

这几年我其实很少直接写 coding 的博文,主要是无聊:浅了没必要重写一遍,深的也不能比别人讲更好。不过最近有同事遇到问题,我刚好有一丁点独创研究,权且记录下来。

假设现在我们负责开发一个后端项目,其中有一个接口,需要接收前端用户上传的文件,然后转发上传到其他后端服务。用户上传的文件在 Java 的 Spring MVC 框架中用 org.springframework.web.multipart.MultipartFile 接收。通常我们会在 Controller 中写一个方法,以 MultipartFile[] 作为入参,接收多个文件。单个文件同理,本文不再赘述。

模板代码如下:

    @PostMapping("/upload")
    public MyResponse<?> upload(MultipartFile[] files) {
        process(files);
        return MyResponse.success(files.length);
    }

如果我们要上传到其他后端服务,当然可以可以将 MultipartFile[] files 所表示的文件写入 File 对象。但这时候往往会向真实磁盘写入文件,有点多此一举。应该可以直接在内存中操作。

再看 Hutool 的上传方法。官方给了一个简单示例,传入 FileUtil#file 构造的 File 对象,声称传文件就像普通表单一样。确实如此。其实 cn.hutool.http.HttpRequest 类提供 form(String name, Resource resource) 方法,允许我们用 multipart/form-data 的方式上传文件,其中 resource 就是文件资源。并且借助这个 cn.hutool.core.io.Resource 类,我们可以构造更复杂的对象。注意不要跟 org.springframework.core.io.Resource 混淆。

Resource 只是接口类,需要寻找实现。打开文档或者 IDE 查找实现类,你可能会想,构造 FileResource 对象就好啦。然而这跟前面说的用 File 对象没什么两样。为了避免多余存盘,我们直接拿输入流转发,所以选择 InputStreamResource。使用构造器 public InputStreamResource(InputStream in, String name),第一个参数的流由 MultipartFile#getInputStream 给出,第二个参数 name 传入文件名,这样就好了。

因为涉及多个文件上传,所以我们组成集合,并包装成 MultiResource,就可以传入 HttpRequest#form 方法了。代码示例如下:

    @PostMapping("/upload")
    public MyResponse<?> upload(MultipartFile[] multipartFiles) {
        if (multipartFiles == null || multipartFiles.length == 0) {
            throw new MessageException("未选择文件");
        }
        MultiResource multiResource = new MultiResource(
            Arrays.stream(multipartFiles)
                    .map(multipartFile -> {
                        try {
                            return new InputStreamResource(multipartFile.getInputStream(), multipartFile.getOriginalFilename());
                        } catch (IOException e) {
                            throw new MessageException("输入流打开失败", e);
                        }
                    }).collect(Collectors.toList())
        );
        HttpResponse httpResponse = HttpRequest.post("https://demo.shansing.net/api/uploadFiles")
            //.timeout(myConfig.getLongTimeout())
            .form("files", multiResource)
            .execute();
        return MyResponse.success(multipartFiles.length);
    }

使用了 Java 8 Stream 语法,如果版本较低改为循环构造即可。

2022-09-05 P.S.修正笔误。

全部为采集文章,文中的 联系方式 均不是 本人 的!

发表评论