Отправка большого zip-файла через REST [duplicate]

Кажется, что это ошибка после обновления зависимостей gradle до 24+. Проверьте этот ответ. Перед этим все мои setError() работали нормально. Кроме того, вы не должны требовать фокуса, чтобы отобразить ошибку.

298
задан nbro 13 May 2016 в 22:04
поделиться

11 ответов

@RequestMapping(value = "/files/{file_name}", method = RequestMethod.GET)
public void getFile(
    @PathVariable("file_name") String fileName, 
    HttpServletResponse response) {
    try {
      // get your file as InputStream
      InputStream is = ...;
      // copy it to response's OutputStream
      org.apache.commons.io.IOUtils.copy(is, response.getOutputStream());
      response.flushBuffer();
    } catch (IOException ex) {
      log.info("Error writing file to output stream. Filename was '{}'", fileName, ex);
      throw new RuntimeException("IOError writing file to output stream");
    }

}

Вообще говоря, когда у вас есть response.getOutputStream(), вы можете что-то там написать. Вы можете передать этот выходной поток в качестве места для размещения генерируемого PDF-файла в генераторе. Кроме того, если вы знаете, какой тип файла вы отправляете, вы можете установить

response.setContentType("application/pdf");
334
ответ дан Jakub Kubrynski 18 August 2018 в 04:58
поделиться
  • 1
    Это в значительной степени то, что я собирался сказать, но вы, вероятно, также должны установить заголовок типа ответа на что-то подходящее для файла. – GaryF 15 April 2011 в 08:01
  • 2
    Да, просто отредактировал сообщение. У меня были разные типы файлов, поэтому я оставил их в браузере, чтобы определить тип содержимого файла по его расширению. – Infeligo 15 April 2011 в 08:05
  • 3
    Любая конкретная причина использовать Apache IOUtils вместо Spring FileCopyUtils? – Powerlord 18 September 2012 в 17:12
  • 4
    Вот лучшее решение: stackoverflow.com/questions/16652760/… – Dmytro Plekhotkin 14 July 2015 в 15:16
  • 5
    мой pdf показывает пустое. – Mr. P 9 June 2016 в 09:15

Если вы:

  • Не хотите загружать весь файл в byte[] перед отправкой в ​​ответ;
  • Хотите / нужно отправить / скачать через InputStream;
  • Хотите иметь полный контроль над типом Mime и именем файла, отправленным
  • У вас есть другие @ControllerAdvice для исключения.

Ниже приведен код, который вам нужен:

@RequestMapping(value = "/stuff/{stuffId}", method = RequestMethod.GET)
public ResponseEntity<InputStreamResource> downloadStuff(@PathVariable int stuffId)
                                                                  throws IOException {
    String fullPath = stuffService.figureOutFileNameFor(stuffId);
    File file = new File(fullPath);

    HttpHeaders respHeaders = new HttpHeaders();
    respHeaders.setContentType("application/pdf");
    respHeaders.setContentLength(12345678);
    respHeaders.setContentDispositionFormData("attachment", "fileNameIwant.pdf");

    InputStreamResource isr = new InputStreamResource(new FileInputStream(file));
    return new ResponseEntity<InputStreamResource>(isr, respHeaders, HttpStatus.OK);
}

Также обратите внимание, что, чтобы избежать прочтения всего файла только для вычисления его длины, лучше сохранить его ранее. Убедитесь, что вы проверяете документы для InputStreamResource .

47
ответ дан acdcjunior 18 August 2018 в 04:58
поделиться
  • 1
    Вы не советуете использовать класс FileSystemResource? – Stephane 4 November 2014 в 23:39
  • 2
    На самом деле, я считаю, что это нормально использовать FileSystemResource. Даже рекомендуется , если ваш ресурс является файлом . В этом примере FileSystemResource можно использовать, где InputStreamResource. – acdcjunior 27 April 2015 в 15:35
  • 3
    О части вычисления длины файла: если вы беспокоитесь, не надо. File#length() должно быть достаточно хорошим в общем случае. Я просто упомянул об этом, потому что он может быть медленным , особенно если файл находится в удаленной системе или что-то более разработанное - база данных, может быть ?. Но только беспокоитесь, если это станет проблемой (или если у вас есть убедительные доказательства, она станет одной), а не раньше. Главное: вы пытаетесь передать файл, если вам нужно предварительно загрузить все это раньше, тогда потоковая передача не имеет никакого значения, а? – acdcjunior 13 May 2015 в 15:21
  • 4
    почему этот код не работает для меня? Он загружает 0 байтов. Я проверил и убедился, что ByteArray & amp; Существуют конвертеры ResourceMessage. Я что-то упускаю ? – coding_idiot 2 November 2015 в 18:01
  • 5
    Почему вы беспокоитесь о ByteArray & amp; Конвертеры ResourceMessage? – acdcjunior 2 November 2015 в 23:44

С Spring 3.0 вы можете использовать возвращаемый объект HttpEntity. Если вы используете это, то вашему контроллеру не нужен объект HttpServletResponse, и поэтому его легче протестировать. Кроме этого, этот ответ относительный, равный значению Infeligo .

Если возвращаемое значение вашей рамки PDF является байтовым массивом (прочитайте вторую часть моей ответ на другие возвращаемые значения) :

@RequestMapping(value = "/files/{fileName}", method = RequestMethod.GET)
public HttpEntity<byte[]> createPdf(
                 @PathVariable("fileName") String fileName) throws IOException {

    byte[] documentBody = this.pdfFramework.createPdf(filename);

    HttpHeaders header = new HttpHeaders();
    header.setContentType(MediaType.APPLICATION_PDF);
    header.set(HttpHeaders.CONTENT_DISPOSITION,
                   "attachment; filename=" + fileName.replace(" ", "_"));
    header.setContentLength(documentBody.length);

    return new HttpEntity<byte[]>(documentBody, header);
}

Если тип возврата вашей PDF-структуры (documentBbody) уже не является массивом байтов (а также нет ByteArrayInputStream ), тогда было бы разумно НЕ сделать это байтовым массивом в первую очередь. Вместо этого лучше использовать:

пример с FileSystemResource:

@RequestMapping(value = "/files/{fileName}", method = RequestMethod.GET)
public HttpEntity<byte[]> createPdf(
                 @PathVariable("fileName") String fileName) throws IOException {

    File document = this.pdfFramework.createPdf(filename);

    HttpHeaders header = new HttpHeaders();
    header.setContentType(MediaType.APPLICATION_PDF);
    header.set(HttpHeaders.CONTENT_DISPOSITION,
                   "attachment; filename=" + fileName.replace(" ", "_"));
    header.setContentLength(document.length());

    return new HttpEntity<byte[]>(new FileSystemResource(document),
                                  header);
}
63
ответ дан herau 18 August 2018 в 04:58
поделиться
  • 1
    -1, это не обязательно загружает весь файл в память, может легко вызвать OutOfMemoryErrors. – Faisal Feroz 7 March 2014 в 14:39
  • 2
    @FaisalFeroz: да, это правильно, но файл документа в любом случае создается в памяти (см. Вопрос: «PDF должен быть сгенерирован в коде»). В любом случае - какое решение вы преодолеваете? – Ralph 7 March 2014 в 19:30
  • 3
    Вы также можете использовать ResponseEntity, который является супер HttpEntity, который позволяет вам указать код ответа HTTP-ответа. Пример: return new ResponseEntity<byte[]>(documentBody, headers, HttpStatus.CREATED) – Amr Mostafa 8 October 2014 в 12:16
  • 4
    @Amr Mostafa: ResponseEntity является подклассом HttpEntity (но я его получаю), с другой стороны, 201 CREATED - это не то, что я буду использовать, когда вернусь только к представлению данных. (см. w3.org/Protocols/rfc2616/rfc2616-sec10.html для 201 СОЗДАНО) – Ralph 8 October 2014 в 13:39
  • 5
    – Alexandru Severin 22 October 2018 в 13:15

То, что я могу быстро подумать, - создать pdf-файл и сохранить его в webapp / downloads / & lt; RANDOM-FILENAME> .pdf и отправьте этот файл вперед, используя HttpServletRequest

request.getRequestDispatcher("/downloads/<RANDOM-FILENAME>.pdf").forward(request, response);

, или если вы можете настроить свой распознаватель видимости как-то как

  <bean id="pdfViewResolver"
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="viewClass"
              value="org.springframework.web.servlet.view.JstlView" />
    <property name="order" value=”2″/>
    <property name="prefix" value="/downloads/" />
    <property name="suffix" value=".pdf" />
  </bean>

, тогда просто верните

return "RANDOM-FILENAME";
5
ответ дан kalyan 18 August 2018 в 04:58
поделиться
  • 1
    Если мне нужны два разрешения, как я могу вернуть имя resolver или выбрать его в контроллере? – azerafati 12 June 2014 в 18:00

Этот код отлично работает, чтобы автоматически загрузить файл с контроллера Spring при нажатии ссылки на jsp.

@RequestMapping(value="/downloadLogFile")
public void getLogFile(HttpSession session,HttpServletResponse response) throws Exception {
    try {
        String filePathToBeServed = //complete file name with path;
        File fileToDownload = new File(filePathToBeServed);
        InputStream inputStream = new FileInputStream(fileToDownload);
        response.setContentType("application/force-download");
        response.setHeader("Content-Disposition", "attachment; filename="+fileName+".txt"); 
        IOUtils.copy(inputStream, response.getOutputStream());
        response.flushBuffer();
        inputStream.close();
    } catch (Exception e){
        LOGGER.debug("Request could not be completed at this moment. Please try again.");
        e.printStackTrace();
    }

}
15
ответ дан rubanbs 18 August 2018 в 04:58
поделиться

Мне удалось передать эту строку, используя встроенную поддержку весной с помощью ResourceHttpMessageConverter. Это установит длину содержимого и тип содержимого, если он сможет определить тип mime

@RequestMapping(value = "/files/{file_name}", method = RequestMethod.GET)
@ResponseBody
public FileSystemResource getFile(@PathVariable("file_name") String fileName) {
    return new FileSystemResource(myService.getFileFor(fileName)); 
}
264
ответ дан Scott Carlson 18 August 2018 в 04:58
поделиться
  • 1
    Это работает. Но файл (.csv-файл) отображается в браузере и не загружается - как я могу заставить браузер загружаться? – chzbrgla 12 July 2013 в 09:26
  • 2
    Вы можете добавить results = MediaType.APPLICATION_OCTET_STREAM_VALUE в @RequestMapping, чтобы принудительно загрузить – David Kago 25 September 2013 в 18:33
  • 3
    Также вы должны добавить & lt; bean class = "org.springframework.http.converter.ResourceHttpMessageConverter" / & gt; в список сообщенийConverters (& lt; mvc: annotation-driven & gt; & lt; mvc: message-converters & gt; ;) – Sllouyssgort 29 November 2013 в 14:15
  • 4
    Есть ли способ установить заголовок Content-Disposition таким образом? – Ralph 15 April 2015 в 12:54
  • 5
    У меня не было необходимости в этом, но я думаю, вы могли бы добавить HttpResponse в качестве параметра к методу, а затем «response.setHeader» («Content-Disposition», «attachment; filename = somefile.pdf») ; & Quot; – Scott Carlson 16 April 2015 в 13:42

Ниже код работал для меня для создания и загрузки текстового файла.

@RequestMapping(value = "/download", method = RequestMethod.GET)
public ResponseEntity<byte[]> getDownloadData() throws Exception {

    String regData = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.";
    byte[] output = regData.getBytes();

    HttpHeaders responseHeaders = new HttpHeaders();
    responseHeaders.set("charset", "utf-8");
    responseHeaders.setContentType(MediaType.valueOf("text/html"));
    responseHeaders.setContentLength(output.length);
    responseHeaders.set("Content-disposition", "attachment; filename=filename.txt");

    return new ResponseEntity<byte[]>(output, responseHeaders, HttpStatus.OK);
}
9
ответ дан Siva Kumar 18 August 2018 в 04:58
поделиться

что-то вроде ниже

@RequestMapping(value = "/download", method = RequestMethod.GET)
public void getFile(HttpServletResponse response) {
    try {
        DefaultResourceLoader loader = new DefaultResourceLoader();
        InputStream is = loader.getResource("classpath:META-INF/resources/Accepted.pdf").getInputStream();
        IOUtils.copy(is, response.getOutputStream());
        response.setHeader("Content-Disposition", "attachment; filename=Accepted.pdf");
        response.flushBuffer();
    } catch (IOException ex) {
        throw new RuntimeException("IOError writing file to output stream");
    }
}

Вы можете отобразить PDF или загрузить его примеры здесь

1
ответ дан xxy 18 August 2018 в 04:58
поделиться

Вы должны иметь возможность напрямую записывать файл в ответ. Что-то вроде

response.setContentType("application/pdf");      
response.setHeader("Content-Disposition", "attachment; filename=\"somefile.pdf\""); 

, а затем напишите файл как двоичный поток на response.getOutputStream(). Не забудьте сделать response.flush() в конце, и это должно сделать это.

69
ответ дан yglodt 18 August 2018 в 04:58
поделиться
  • 1
    это не «весенний» способ установить такой тип контента? @RequestMapping(value = "/foo/bar", produces = "application/pdf") – Black 7 May 2014 в 06:44
  • 2
    @Francis, что, если ваше приложение загружает разные типы файлов? Ответ Lobster1234 позволяет вам динамически устанавливать настройку содержимого. – Rose 13 November 2015 в 03:29
  • 3
    это правда @Rose, но я считаю, что лучше было бы определить разные конечные точки в каждом формате – Black 13 November 2015 в 04:10
  • 4
    Наверное, нет, потому что он не масштабируемый. В настоящее время мы поддерживаем десяток видов ресурсов. Мы могли бы поддерживать больше типов файлов, исходя из того, что пользователи хотят загружать, в этом случае мы могли бы получить так много конечных точек, по существу делающих то же самое. IMHO должна быть только одна конечная точка загрузки, и она обрабатывает множество типов файлов. @Francis – Rose 13 November 2015 в 06:12
  • 5
    это абсолютно «масштабируемое», но мы можем согласиться не согласиться с тем, является ли это наилучшей практикой – Black 16 November 2015 в 23:42
0
ответ дан Jorge Santos Neill 6 September 2018 в 19:18
поделиться
0
ответ дан Jorge Santos Neill 30 October 2018 в 00:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: