Отправка файла между системами A-> B-> C без сохранения всего файла в B

У меня есть 3 отдельных веб-приложения spring

  • A использует spring 4.x
  • B использует spring 3.2.0
  • C использует spring 4.x

B и C предоставляет контроллеры REST для загрузки файлов

  • A читает файл и загружает его в B
  • B отправляет запрос C без необходимости читать содержимое файла
  • а затем C делает все, что захочет, с файлом.

Таким образом, поток будет A- > B- > C

Мой вопрос: возможно ли установить B таким образом, чтобы B не сохранял весь файл в памяти, но читал бы входящий поток и переместите его на C?

Что мне удалось сделать: А

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);
}

В

@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";
}

С

@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";
}

Я могу легко копировать файлы более > 10 ГБ с A на C с помощью B.

С таким решением мы можем попытаться остановить A во время передачи, B и C должны быть уведомлены об ошибке, но иногда бывает так, что сообщение об ошибке не достигает C - он закрывается с исключением тайм-аута сокета, любая идея почему это происходит и как правильно его реализовать?

Является ли этот подход действительным или его можно обработать лучше?

Ответы

Ответ 1

Я попытался бы установить меньший тайм-аут сокета на C, чем у вас на B. В настоящее время кажется, что оба имеют значение по умолчанию, поэтому, если A зависает, оба B и C перестанут получать данные почти в одно и то же время. Оба запускают тайм-аут, и, возможно, это состояние гонки, где это зависит от точности таймаута, которая в первый раз заканчивается.

Ответ 2

Вы думали о Flume? Из того, что я вижу, проблема заключается в том, чтобы не хранить его в B и файлах, я полагаю, что они могут быть большими. В таких случаях, почему бы не использовать поток Apache Flume для потоковой передачи файлов непосредственно на канал, а затем в ваш приемник, который будет вашим B, и выгрузите его в конечный пункт назначения "C". Однако большой файл, эта арка помогает.

Как сказал кто-то: "Если мои знания ограничены проблемой, которую только вы видите, я могу быть только средством для решения, а не самим решением"