Проблемы при загрузке файла - PullRequest
0 голосов
/ 04 августа 2020

Я пытаюсь загрузить файл из Angular UI, даже после того, как я получил исключение в поддерживаемом коде, я все равно получаю 200 ok в качестве ответа.

Вот код, который у меня есть:

public ResponseEntity<Object> downloadDocument(@PathVariable("docId") Long docId,
            HttpServletResponse response) { 

OutPutStream outputStream = null;

try {
outputStream = response.getOutputStream();
docService.downloadDocument(docId,outputStream);
return ResponseEntity.ok().contentType(MediaType.parseMediaType(MediaType.APPLICATION_OCTET_STREAM_VALUE))
                    .body("Success");
} catch(Exception e) {
return ResponseEntity.badRequest().body(e.getMessage());
} finally {
if (Objects.nonNull(outputStream)) {
                IOUtils.closeQuietly(outputStream);
            }
}

Не могли бы вы помочь мне разобраться, что здесь творится?

1 Ответ

1 голос
/ 04 августа 2020

Когда вы открываете поток вывода, отправляются заголовки. период.

С этого момента нет возврата. Вы не можете сначала открыть выходной поток, а потом - go: Ой, подождите! Нет! неважно! плохой запрос!

Вот как это работает - вы выбираете сторону и придерживаетесь ее. Вы можете:

  1. Сделать это самостоятельно; используйте response и доступные там методы для настройки ответа; вы можете установить заголовки, код возврата и сообщение, а также получить выходной поток для тела ответа и таким образом отправлять данные. Если вы сделаете это, вы не сможете ТАКЖЕ вернуть ResponseEntity!
  2. Do НЕ даже добавить параметр HttpServletResponse и вместо этого вернуть объект ResponseEntity.

Вы делаете и то, и другое, что запрещено.

Я искренне удивлен; spring немного сломан и должен генерировать исключения здесь, поскольку он не может обслуживать то, что вы просите его здесь.

NB: Обратите внимание, что тип исключения обычно более информативен, чем сообщение (многие исключения даже не имеют сообщение).

Собираем все вместе:

public ResponseEntity<?> downloadDocument(@PathVariable("docId") Long docId) { 

  try {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    docService.downloadDocument(docId, baos);
    return ResponseEntity.ok()
      .contentType(MediaType.parseMediaType(MediaType.APPLICATION_OCTET_STREAM_VALUE))
      .body(baos.toByteArray());
  } catch(Exception e) {
    return ResponseEntity.badRequest().body(e.getMessage());
  }
}

'toString' по умолчанию печатает собственный тип, если есть не является сообщением, а его собственный тип плюс сообщение, если оно есть, поэтому вы получаете, например:

NullPointerException

или

NullPointerException: parameter foo

, что вы хотите (вместо буквальной пустой строки в первом случае и просто «параметр foo» во втором, что тоже не особо информативно).

сообщения обычно не обязательно имеют смысл без контекста типа исключения.

NB: это кэширует все содержимое загруженного документа в память сервера перед его отправкой. Если документ большой, это плохая идея, но если вы хотите «передать» его, у вас есть довольно серьезная проблема, присущая протоколу HTTP : как только вы начнете отправлять, вы уже отправил «статус ошибки» (т.е. вы уже отправили 200 OK), поэтому, если процесс загрузки документа вызывает исключение на полпути, вы не можете go назад и отправить сообщение об ошибке. Вам нужен какой-то проводной протокол, по которому вы отправляете тело по частям и у вас есть код типа, который вы отправляете, чтобы получатель мог сканировать их и знал, что `` этот тип означает, что идет больше данных '', и `` этот тип означает произошла ошибка, и теперь я могу прочитать описание ошибки ». Все становится довольно сложно. Кешируя это, вы избегаете этого беспорядка. Но если этот документ может быть очень большим, вам придется иметь дело с вышеуказанным, быстрых исправлений нет.

...