这几年我其实很少直接写 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.修正笔误。
好 文章
好评解决问题
牛蛙!!!!!!楼主真棒!关注博主了
太强了!!!!完美解决!!!