Springboot请求上传附件接口的两种方式(JDK17)

154 阅读2分钟

生成临时文件

项目中,有一个业务,需要请求一个三方的https接口,上传一个.dcm文件,返回一个png文件。这个.dcm文件,会经过我们自己的Springboot服务,然后调用第三方的https接口。一开始我们使用的方法是:上传一个.dcm文件到我们的Springboot服务,然后生成一个临时文到客户分配OpenShift的PVC文件夹中,再通过这个临时文件构建FileSystemResource,去请求第三方的接口

// 模拟代码如下
void contextLoads() {
        //模拟当时流程,上传一个.dcm问津,获取一个文件流。
        try (FileInputStream fileInputStream = new FileInputStream(filePath)) {
            // 拿到文件流后会生成一个临时文件
            File template = File.createTempFile("template", ".dcm",new File(PVC_Path));
            // 写入临时文件
            try (FileOutputStream fileOutputStream = new FileOutputStream(template)) {
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = fileInputStream.read(buffer)) != -1) {
                    fileOutputStream.write(buffer, 0, bytesRead);
                }
                // 模拟请求第三方的接口,返回图片
                WebClient webClient = WebClient.create("http://localhost:8080/file");
                // 构建 multipart 请求体
                MultipartBodyBuilder builder = new MultipartBodyBuilder();
                builder.part("file", new FileSystemResource(template));
               
                // 请求第三方的接口
                Mono<Resource> resourceMono = webClient.post()
                        .uri("/upload")
                        .contentType(MediaType.MULTIPART_FORM_DATA)
                        .body(BodyInserters.fromMultipartData(builder.build()))
                        .retrieve()
                        .bodyToMono(Resource.class);

                resourceMono.subscribe(resource -> {
                    log.info("接收到了文件流");
                });
            } catch (Exception e) {
                throw new RuntimeException(e);
            }finally {
                // 删除临时文件
                template.deleteOnExit();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

绕过生成临时文件,通过ByteArrayResource调用附件上传接口

然而,以外发生了。。。返回的.png文件,自己单元测试都是ok的,但是放到OpenShift中,图片就只有几K(正常应该是在1M左右),上PVC排查,发现有0K的临时文件,怀疑上传到PVC时,有异常。删掉template.deleteOnExit(),上PVC产看临时文件,果然是0K。由于我们的权限有限,最多看下pod的日志和pod内部的环境。所以有了第二种上传附件的方式

// 尝试绕过在PVC创建临时文件,既然我们能拿到文件,就通过文件流的形式请求接口
void uploadWithByte() {
        //模拟当时流程,会从一个https的请求,获取一个流,这里用文件流替代。
        try (FileInputStream fileInputStream = new FileInputStream(filePath)) {
            // 直接把文件流转成byte[]
            byte[] fileBytes = fileInputStream.readAllBytes();
            // 模拟请求第三方的接口,返回图片
            WebClient webClient = WebClient.create("http://localhost:8080/file");
            // 构建 multipart 请求体
            MultipartBodyBuilder builder = new MultipartBodyBuilder();
            // 构建ByteArrayResource
            builder.part("file", new ByteArrayResource(fileBytes){
                @Override
                public String getFilename() {
                    return "template.dcm";
                }
            });
            // 请求第三方的接口
            Mono<Resource> resourceMono = webClient.post()
                    .uri("/upload")
                    .contentType(MediaType.MULTIPART_FORM_DATA)
                    .body(BodyInserters.fromMultipartData(builder.build()))
                    .retrieve()
                    .bodyToMono(Resource.class);

            resourceMono.subscribe(resource -> {
                log.info("接收到了文件流");
            });
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

通过这种方式,可以无需在PVC中生成临时文件。而且代码也简单很多