Как установить "Content-Disposition" и "Filename" при использовании FileSystemResource, чтобы заставить файл загружать файл?

Каков наиболее подходящий и стандартный способ установки Content-Disposition=attachment и filename=xyz.zip с помощью Spring 3 FileSystemResource?

Действие выглядит так:

@ResponseBody
@RequestMapping(value = "/action/{abcd}/{efgh}", method = RequestMethod.GET, produces = "application/zip")
@PreAuthorize("@authorizationService.authorizeMethod()")
public FileSystemResource doAction(@PathVariable String abcd, @PathVariable String efgh) {

    File zipFile = service.getFile(abcd, efgh);

    return new FileSystemResource(zipFile);
}

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

Могут быть обходные пути для этой проблемы, но я хотел бы знать правильный способ Spring и FileSystemResource для достижения этой цели.

P.S. Файл, который используется здесь, является временным файлом, помеченным для удаления, когда существует JVM.

Ответ 1

@RequestMapping(value = "/action/{abcd}/{efgh}", method = RequestMethod.GET)
@PreAuthorize("@authorizationService.authorizeMethod(#id)")
public HttpEntity<byte[]> doAction(@PathVariable ObjectType obj, @PathVariable Date date, HttpServletResponse response) throws IOException {
    ZipFileType zipFile = service.getFile(obj1.getId(), date);

    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
    response.setHeader("Content-Disposition", "attachment; filename=" + zipFile.getFileName());

    return new HttpEntity<byte[]>(zipFile.getByteArray(), headers);
}

Ответ 2

 @RequestMapping(value = "/files/{file_name}", method = RequestMethod.GET)
    @ResponseBody
    public FileSystemResource getFile(@PathVariable("file_name") String fileName,HttpServletResponse response) {
        response.setContentType("application/pdf");      
        response.setHeader("Content-Disposition", "attachment; filename=somefile.pdf"); 
        return new FileSystemResource(new File("file full path")); 
    }

Ответ 3

Вот альтернативный подход для Spring 4. Обратите внимание, что этот пример явно не использует хорошие методы доступа к файловой системе, это просто для демонстрации того, как некоторые свойства могут быть установлены декларативно.

@RequestMapping(value = "/{resourceIdentifier}", method = RequestMethod.GET, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
// @ResponseBody // Needed for @Controller but not for @RestController.
public ResponseEntity<InputStreamResource> download(@PathVariable(name = "resourceIdentifier") final String filename) throws Exception
{
    final String resourceName = filename + ".dat";
    final File iFile = new File("/some/folder", resourceName);
    final long resourceLength = iFile.length();
    final long lastModified = iFile.lastModified();
    final InputStream resource = new FileInputStream(iFile);

    return ResponseEntity.ok()
            .header("Content-Disposition", "attachment; filename=" + resourceName)
            .contentLength(resourceLength)
            .lastModified(lastModified)
            .contentType(MediaType.APPLICATION_OCTET_STREAM_VALUE)
            .body(resource);
}

Ответ 4

Я внес несколько изменений в оба приведенных ответа, и я получил лучшее из обоих в своем проекте, где мне нужно было извлечь изображение из базы данных в виде большого двоичного объекта и затем передать его клиентам:

@GetMapping("/images/{imageId:.+}")
@ResponseBody
public ResponseEntity<FileSystemResource>  serveFile(@PathVariable @Valid String imageId,HttpServletResponse response)
{       
    ImageEntity singleImageInfo=db.storage.StorageService.getImage(imageId);
    if(singleImageInfo==null)
    {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
    }
    Blob image=singleImageInfo.getImage();
    try {           
        String filename= UsersExtra.GenerateSession()+"xxyy"+singleImageInfo.getImage1Ext().trim();

    byte [] array = image.getBytes( 1, ( int ) image.length() );
    File file = File.createTempFile(UsersExtra.GenerateSession()+"xxyy", singleImageInfo.getImage1Ext().trim(), new File("."));
    FileOutputStream out = new FileOutputStream( file );
    out.write( array );
    out.close();
    FileSystemResource testing=new FileSystemResource(file);

    String mimeType = "image/"+singleImageInfo.getImage1Ext().trim().toLowerCase().replace(".", "");
      response.setContentType(mimeType);    

        String headerKey = "Content-Disposition";
       String headerValue = String.format("attachment; filename=\"%s\"", filename);
       response.setHeader(headerKey, headerValue);
      // return new FileSystemResource(file); 
       return ResponseEntity.status(HttpStatus.OK).body( new FileSystemResource(file));
    }catch(Exception e)
    {
        System.out.println(e.getMessage());
    }
    return null;
}

Использование ResponseEntity в коде Kumar поможет вам ответить с правильным кодом ответа. Примечание: преобразование из большого двоичного объекта в файл цитируется по этой ссылке: фрагмент кода для создания файла из содержимого большого двоичного объекта в Java.

Ответ 5

В дополнение к принятому ответу, Spring имеет класс ContentDisposition, специфичный для этой цели. Я считаю, что это имеет дело с очисткой имени файла.

      ContentDisposition contentDisposition = ContentDisposition.builder("inline")
          .filename("Filename")
          .build();

      HttpHeaders headers = new HttpHeaders();
      headers.setContentDisposition(contentDisposition);