在系统 A->B->C 之间发送文件,而不将整个文件存储在 B 中

2022-09-04 04:22:10

我有3个单独的弹簧Web应用程序

  • A使用弹簧4.x
  • B 使用弹簧 3.2.0
  • C 使用弹簧 4.x

BC 公开了用于上传文件的 REST 控制器

  • A 读取文件并将其上载到 B
  • B 将请求发送到 C,无需读取文件内容
  • 然后C对文件做任何它想要的操作。

因此,流量将为 A->B->C

我的问题是 - 是否可以以这种方式设置B,以便B不会将整个文件存储在内存中,而是读取传入的流并将其转发到C

我设法做的是:A

public void sendFileFromA() throws FileNotFoundException {
    final InputStream fis = new FileInputStream(new File("someFile"));
    final RequestCallback requestCallback = new RequestCallback() {
        @Override
        public void doWithRequest(final ClientHttpRequest request) throws IOException {
            request.getHeaders().add("Content-type", "application/octet-stream");
            IOUtils.copy(fis, request.getBody());
        }
    };
    final RestTemplate restTemplate = new RestTemplate();
    SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
    requestFactory.setBufferRequestBody(false);
    restTemplate.setRequestFactory(requestFactory);

    final HttpMessageConverterExtractor<String> responseExtractor = new HttpMessageConverterExtractor<>(
            String.class, restTemplate.getMessageConverters());
    restTemplate.execute("http://b_url/upload", HttpMethod.POST, requestCallback, responseExtractor);
}

B

@RequestMapping(value = "/upload", method = RequestMethod.POST)
public @ResponseBody String handleFileUpload(HttpServletRequest request) throws IOException {
    final ServletInputStream input = request.getInputStream();

    final RequestCallback requestCallback = new RequestCallback() {
        @Override
        public void doWithRequest(final ClientHttpRequest request) throws IOException {
            request.getHeaders().add("Content-type", "application/octet-stream");
            try (OutputStream body = request.getBody()) {
                IOUtils.copy(input, body);
            }
        }
    };
    final RestTemplate restTemplate = new RestTemplate();
    SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
    requestFactory.setBufferRequestBody(false);
    restTemplate.setRequestFactory(requestFactory);

    final HttpMessageConverterExtractor<String> responseExtractor = new HttpMessageConverterExtractor<>(
            String.class, restTemplate.getMessageConverters());
    restTemplate.execute("http://c_url/upload", HttpMethod.POST, requestCallback, responseExtractor);

    return "success";
}

C

@RequestMapping(value = "/upload", method = RequestMethod.POST)
public @ResponseBody String handleFileUpload(HttpServletRequest request) throws IOException {
    ServletInputStream input = request.getInputStream();

    try (BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream("zibiTest"))) {
        IOUtils.copy(input, output);
    }
    return "success";
}

我可以使用B轻松地将超过>10GB的文件从A复制到C。

使用这样的解决方案,我们可以尝试在传输时停止A,B和C应该收到有关错误的通知,但有时会发生错误消息未到达C的情况 - 它会以套接字超时异常关闭,任何想法为什么会发生这种情况以及如何正确实现它?

这是一种有效的方法,还是可以更好地处理?


答案 1

我会尝试在 C 上设置比在 B 上更小的套接字超时。目前,似乎两者都有一些默认值,因此如果 A 挂起,B 和 C 将几乎同时停止获取数据。两者都开始超时,也许这是一个竞态条件,它取决于超时精度,其中一个先超时。


答案 2

推荐