Скачивание файла с пружинных контроллеров - PullRequest
342 голосов
/ 15 апреля 2011

У меня есть требование, где мне нужно скачать PDF с сайта.PDF должен быть сгенерирован в коде, который, как я думал, будет представлять собой комбинацию freemarker и фреймворка для создания PDF, такого как iText.Есть ли лучший способ?

Однако моя основная проблема заключается в том, как разрешить пользователю загружать файл через Spring Controller?

Ответы [ 11 ]

366 голосов
/ 15 апреля 2011
@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");
281 голосов
/ 31 мая 2012

Мне удалось поточить это, используя встроенную поддержку Spring с ResourceHttpMessageConverter. Это установит content-length и content-type, если он может определить mime-type

@RequestMapping(value = "/files/{file_name}", method = RequestMethod.GET)
@ResponseBody
public FileSystemResource getFile(@PathVariable("file_name") String fileName) {
    return new FileSystemResource(myService.getFileFor(fileName)); 
}
75 голосов
/ 15 апреля 2011

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

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

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

72 голосов
/ 29 октября 2012

В 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 Framework (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);
}
57 голосов
/ 24 октября 2014

Если вы:

  • Не хотите загружать весь файл в 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.

18 голосов
/ 28 ноября 2013

Этот код прекрасно работает для автоматической загрузки файла с контроллера 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();
    }

}
12 голосов
/ 29 декабря 2015

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

@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);
}
5 голосов
/ 15 апреля 2011

Что я могу быстро придумать, так это сгенерировать pdf и сохранить его в файле webapp / downloads / .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";
2 голосов
/ 15 июня 2016

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

@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 голос
/ 30 августа 2018

У меня работает следующее решение

    @RequestMapping(value="/download")
    public void getLogFile(HttpSession session,HttpServletResponse response) throws Exception {
        try {

            String fileName="archivo demo.pdf";
            String filePathToBeServed = "C:\\software\\Tomcat 7.0\\tmpFiles\\";
            File fileToDownload = new File(filePathToBeServed+fileName);

            InputStream inputStream = new FileInputStream(fileToDownload);
            response.setContentType("application/force-download");
            response.setHeader("Content-Disposition", "attachment; filename="+fileName); 
            IOUtils.copy(inputStream, response.getOutputStream());
            response.flushBuffer();
            inputStream.close();
        } catch (Exception exception){
            System.out.println(exception.getMessage());
        }

    }
...