1

Ich habe ein Servlet, das ein Bild als InputStreamResource zurückgibt. Es gibt ca. 50 statische Bilder, die basierend auf einigen get query Parametern zurückgegeben werden sollen.Wie man eine InputStreamResource in RestController zwischenspeichern?

Um nicht jedes dieser Bilder jedes Mal nachschlagen zu müssen (was sehr oft ist), möchte ich diese Bilder-Antworten zwischenspeichern.

@RestController 
public class MyRestController { 
    //code is just example; may be any number of parameters 
    @RequestMapping("/{code}") 
    @Cachable("code.cache") 
    public ResponseEntity<InputStreamResource> getCodeLogo(@PathVariable("code") String code) { 
     FileSystemResource file = new FileSystemResource("d:/images/" + code + ".jpg"); 
     return ResponseEntity.ok() 
      .contentType("image/jpg") 
      .lastModified(file.lastModified()) 
      .contentLength(file.contentLength()) 
      .body(new InputStreamResource(file.getInputStream())); 

    } 
} 

Bei Verwendung der @Cacheable Annotation (egal ob direkt auf dem RestMapping Verfahren oder an einen externen Dienstleister Refactoring), I_'m die folgende Ausnahme erhalten:

cause: java.lang.IllegalStateException: InputStream has already been read - do not use InputStreamResource if a stream needs to be read multiple times - error: InputStream has already been read - do not use InputStreamResource if a stream needs to be read multiple times 
org.springframework.core.io.InputStreamResource.getInputStream(InputStreamResource.java:96) 
org.springframework.http.converter.ResourceHttpMessageConverter.writeInternal(ResourceHttpMessageConverter.java:100) 
org.springframework.http.converter.ResourceHttpMessageConverter.writeInternal(ResourceHttpMessageConverter.java:47) 
org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:195) 
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:238) 
org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor.handleReturnValue(HttpEntityMethodProcessor.java:183) 
org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:81) 
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:126) 
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:832) 
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:743) 

Frage: Wie kann Ich cache dann die ResponseEntity vom Typ InputStreamResource überhaupt?

+0

Muss es nur einmal gelesen werden? Oder sollte die Datei auf dem Datenträger auf Änderungen überwacht werden? – Niels

+0

Ich möchte '@ EvictCache' in festen Zeiträumen anrufen, zB nachts oder wöchentlich. Im Allgemeinen sollte es nur einmal vorbereitet und dann wiederverwendet werden, ohne dass die Datei lokal nachgeschlagen werden muss. – membersound

+3

Löschen. Sonst könnte man die Datei während der Initialisierung des Controllers einmal gelesen haben und sie in eine ByteArrayResource laden ... – Niels

Antwort

2

Cache-Manager wird zum Cache ResponseEntity mit InputStreamResource innerhalb es hinzufügen. Zum ersten Mal wird es in Ordnung sein. Aber wenn zwischengespeichert ResponseEntity wird versuchen, InputStreamResouce zweites Mal zu lesen, erhalten Sie eine Ausnahme, weil es nicht mehr als einmal lesen kann.

Lösung: Cache InputStreamResouce nicht selbst, aber Cache den Inhalt des Streams.

@RestController 
public class MyRestController {  
    @RequestMapping("/{code}") 
    @Cachable("code.cache") 
    public ResponseEntity<byte[]> getCodeLogo(@PathVariable("code") String code) { 
     FileSystemResource file = new FileSystemResource("d:/images/" + code + ".jpg"); 

     byte [] content = new byte[(int)file.contentLength()]; 
     IOUtils.read(file.getInputStream(), content); 

     return ResponseEntity.ok() 
      .contentType(MediaType.IMAGE_JPEG) 
      .lastModified(file.lastModified()) 
      .contentLength(file.contentLength()) 
      .body(content); 
    } 
} 

habe ich IOUtils.read() von org.apache.commons.io, zu kopieren Bytes aus dem Stream zu Array verwendet, aber man kann es durch eine bevorzugte Art und Weise tun.

1

Sie können keine Streams zwischenspeichern. Sobald sie gelesen sind, sind sie weg. Die Fehlermeldung darüber ziemlich klar ist:

InputStream has already been read - 
do not use InputStreamResource if a stream needs to be read multiple times 

von Code und Kommentare, so scheint es mir, dass Sie einen großen images Ordner mit JPG-Logos haben (die hinzugefügt werden könnten, gelöscht oder geändert), und Sie wollen eine haben täglich Cache von denen Sie gefragt werden, so dass Sie sie nicht ständig von der Festplatte neu laden müssen.

Wenn das der Fall ist, ist es die beste Option, den Inhalt der Datei in einem ByteArray zu lesen und stattdessen zu cachen/zurückzugeben.

+0

Ihre Annahmen sind völlig richtig. – membersound

Verwandte Themen