Spring MVC: come restituire l'immagine in @ResponseBody?


142

Ricevo dati di immagine (come byte[]) da DB. Come restituire questa immagine @ResponseBody?

MODIFICARE

L'ho fatto senza @ResponseBodyusare HttpServletResponsecome parametro del metodo:

@RequestMapping("/photo1")
public void photo(HttpServletResponse response) throws IOException {
    response.setContentType("image/jpeg");
    InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
    IOUtils.copy(in, response.getOutputStream());
}

L'uso @ResponseBodycon il org.springframework.http.converter.ByteArrayHttpMessageConverterconvertitore registrato come ha detto @Sid non funziona per me :(.

@ResponseBody
@RequestMapping("/photo2")
public byte[] testphoto() throws IOException {
    InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
    return IOUtils.toByteArray(in);
}

Risposte:


97

se si utilizza la versione Spring di 3.1 o successive, è possibile specificare "produce" nell'annotazione @RequestMapping. L'esempio che segue funziona per me fuori dagli schemi. Non è necessario il convertitore di registro o qualsiasi altra cosa se si abilita web mvc ( @EnableWebMvc).

@ResponseBody
@RequestMapping(value = "/photo2", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
public byte[] testphoto() throws IOException {
    InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
    return IOUtils.toByteArray(in);
}

78

Con Spring 4.1 e versioni successive, puoi restituire praticamente qualsiasi cosa (come immagini, pdf, documenti, barattoli, zip, ecc.) Semplicemente senza dipendenze extra. Ad esempio, il seguente potrebbe essere un metodo per restituire l'immagine del profilo di un utente da MongoDB GridFS:

@RequestMapping(value = "user/avatar/{userId}", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<InputStreamResource> downloadUserAvatarImage(@PathVariable Long userId) {
    GridFSDBFile gridFsFile = fileService.findUserAccountAvatarById(userId);

    return ResponseEntity.ok()
            .contentLength(gridFsFile.getLength())
            .contentType(MediaType.parseMediaType(gridFsFile.getContentType()))
            .body(new InputStreamResource(gridFsFile.getInputStream()));
}

Le cose da notare:

  • ResponseEntity con InputStreamResource come tipo di ritorno

  • Creazione dello stile del builder ResponseEntity

Con questo metodo non devi preoccuparti di eseguire l'autowiring in HttpServletResponse, lanciare un'eccezione IOException o copiare i dati del flusso in giro.


1
Questo genera la seguente eccezione, come stai serializzando MyInputStream ?: Impossibile scrivere il contenuto: Nessun serializzatore trovato per la classe com.mongodb.gridfs.GridFSDBFile $ MyInputStream
Nestor Ledon

Mentre questo principalmente come esempio di ciò che potresti eventualmente fare, Mongo-Java-Driver 3.0.3 con GridFsDBFile.getInputStream () non restituisce una classe anonima chiamata MyInputStream. Verificherei le tue versioni - forse aggiornerai?
Jaymes Bearden,

4
Mi piace come questo flusso il file invece di copiare tutto in memoria. Vedere anche stackoverflow.com/questions/20333394/...
Wim Deblauwe

60

Oltre a registrare un ByteArrayHttpMessageConverter, potresti voler usare un ResponseEntityinvece di @ResponseBody. Il seguente codice funziona per me:

@RequestMapping("/photo2")
public ResponseEntity<byte[]> testphoto() throws IOException {
    InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");

    final HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.IMAGE_PNG);

    return new ResponseEntity<byte[]>(IOUtils.toByteArray(in), headers, HttpStatus.CREATED);
}

16

Utilizzando Spring 3.1.xe 3.2.x, ecco come dovresti farlo:

Il metodo del controller:

@RequestMapping("/photo2")
public @ResponseBody byte[] testphoto() throws IOException {
    InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
    return IOUtils.toByteArray(in);
}

E l'annotazione mvc nel file servlet-context.xml:

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter">
            <property name="supportedMediaTypes">
                <list>
                    <value>image/jpeg</value>
                    <value>image/png</value>
                </list>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

13

Oltre a un paio di risposte qui alcuni suggerimenti (primavera 4.1).

Nel caso in cui non abbiate alcun configuratore di messaggistica configurato in WebMvcConfig, avendo ResponseEntityal suo interno funzioni @ResponseBodybene.

Se lo fai, cioè hai un MappingJackson2HttpMessageConverterconfigurato (come me) usando i ResponseEntityritorni a org.springframework.http.converter.HttpMessageNotWritableException.

L'unica soluzione a lavorare in questo caso è quello di avvolgere un byte[]nel @ResponseBodymodo seguente:

@RequestMapping(value = "/get/image/{id}", method=RequestMethod.GET, produces = MediaType.IMAGE_PNG_VALUE)
public @ResponseBody byte[] showImageOnId(@PathVariable("id") String id) {
    byte[] b = whatEverMethodUsedToObtainBytes(id);
    return b;
}

In questo caso, ricorda di configurare correttamente i convertitori di messaggi (e aggiungere un ByteArrayHttpMessageConverer) nel tuo WebMvcConfig, in questo modo:

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    converters.add(mappingJackson2HttpMessageConverter());
    converters.add(byteArrayHttpMessageConverter());
}

@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    converter.setObjectMapper(objectMapper);
    return converter;
}

@Bean
public ByteArrayHttpMessageConverter byteArrayHttpMessageConverter() {
    ByteArrayHttpMessageConverter arrayHttpMessageConverter = new ByteArrayHttpMessageConverter();
    arrayHttpMessageConverter.setSupportedMediaTypes(getSupportedMediaTypes());
    return arrayHttpMessageConverter;
}

private List<MediaType> getSupportedMediaTypes() {
    List<MediaType> list = new ArrayList<MediaType>();
    list.add(MediaType.IMAGE_JPEG);
    list.add(MediaType.IMAGE_PNG);
    list.add(MediaType.APPLICATION_OCTET_STREAM);
    return list;
}

6

Nel contesto dell'applicazione dichiarare un AnnotationMethodHandlerAdapter e registerByteArrayHttpMessageConverter:

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
  <property name="messageConverters">
    <util:list>
      <bean id="byteArrayMessageConverter" class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
    </util:list>
  </property>
</bean> 

anche nel metodo del gestore impostare il tipo di contenuto appropriato per la risposta.


@jsinghfoss si riferisce alla risposta migliore.
Peymankh,

6
 @RequestMapping(value = "/get-image",method = RequestMethod.GET)
public ResponseEntity<byte[]> getImage() throws IOException {
    RandomAccessFile f = new RandomAccessFile("/home/vivex/apache-tomcat-7.0.59/tmpFiles/1.jpg", "r");
    byte[] b = new byte[(int)f.length()];
    f.readFully(b);
    final HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.IMAGE_PNG);


    return new ResponseEntity<byte[]>(b, headers, HttpStatus.CREATED);



}

Ha funzionato per me.


5

Preferisco questo:

private ResourceLoader resourceLoader = new DefaultResourceLoader();

@ResponseBody
@RequestMapping(value = "/{id}",  produces = "image/bmp")
public Resource texture(@PathVariable("id") String id) {
    return resourceLoader.getResource("classpath:images/" + id + ".bmp");
}

Cambia il tipo di supporto in qualunque formato immagine tu abbia.


1
Buona chiamata ResourceLoader, ma costruire un percorso dall'input esterno come nel tuo esempio è una cattiva idea: cwe.mitre.org/data/definitions/22.html
qerub

3

Per me funziona nella primavera 4.

@RequestMapping(value = "/image/{id}", method = RequestMethod.GET)
public void findImage(@PathVariable("id") String id, HttpServletResponse resp){

        final Foto anafoto = <find object>
        resp.reset();
        resp.setContentType(MediaType.IMAGE_JPEG_VALUE);
        resp.setContentLength(anafoto.getImage().length);

        final BufferedInputStream in = new BufferedInputStream(new ByteArrayInputStream(anafoto.getImageInBytes()));

        try {
            FileCopyUtils.copy(in, resp.getOutputStream());
            resp.flushBuffer();
        } catch (final IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

}

2

Nessuna delle risposte ha funzionato per me, quindi sono riuscito a farlo in questo modo:

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType("your content type here"));
headers.set("Content-Disposition", "attachment; filename=fileName.jpg");
headers.setContentLength(fileContent.length);
return new ResponseEntity<>(fileContent, headers, HttpStatus.OK);

Impostando l' Content-Dispositionintestazione sono stato in grado di scaricare il file con l' @ResponseBodyannotazione sul mio metodo.


2

È necessario specificare il tipo di supporto nella risposta. Sto usando un'annotazione @GetMapping con produce = MediaType.IMAGE_JPEG_VALUE. @RequestMapping funzionerà allo stesso modo.

@GetMapping(value="/current/chart",produces = MediaType.IMAGE_JPEG_VALUE)
@ResponseBody
public byte[] getChart() {
    return ...;
}

Senza un tipo di supporto, è difficile indovinare cosa viene effettivamente restituito (include chiunque legga il codice, il browser e, naturalmente, lo stesso Spring). Un byte [] non è specifico. L'unico modo per determinare il tipo di supporto da un byte [] è annusare e indovinare.

Fornire un tipo di supporto è solo la migliore pratica


Funziona per me in Spring Boot 2.x. Grazie per aver condiviso.
attacomsian,

1

Ecco come lo faccio con Spring Boot e Guava:

@RequestMapping(value = "/getimage", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
public void getImage( HttpServletResponse response ) throws IOException
{
    ByteStreams.copy( getClass().getResourceAsStream( "/preview-image.jpg" ), response.getOutputStream() );
}

0

Nella primavera 4 è molto semplice non è necessario apportare modifiche ai bean. Contrassegna solo il tipo di ritorno su @ResponseBody.

Esempio:-

@RequestMapping(value = "/image/{id}")
    public @ResponseBody
    byte[] showImage(@PathVariable Integer id) {
                 byte[] b;
        /* Do your logic and return 
               */
        return b;
    }

1
Il problema che ho riscontrato è che il tipo di contenuto non è impostato correttamente.
ETL

0

Penso che potresti aver bisogno di un servizio per archiviare il caricamento dei file e ottenere quel file. Controlla maggiori dettagli da qui

1) Creare un servizio di archiviazione

@Service
public class StorageService {

Logger log = LoggerFactory.getLogger(this.getClass().getName());
private final Path rootLocation = Paths.get("upload-dir");

public void store(MultipartFile file) {
    try {
        Files.copy(file.getInputStream(), this.rootLocation.resolve(file.getOriginalFilename()));
    } catch (Exception e) {
        throw new RuntimeException("FAIL!");
    }
}

public Resource loadFile(String filename) {
    try {
        Path file = rootLocation.resolve(filename);
        Resource resource = new UrlResource(file.toUri());
        if (resource.exists() || resource.isReadable()) {
            return resource;
        } else {
            throw new RuntimeException("FAIL!");
        }
    } catch (MalformedURLException e) {
        throw new RuntimeException("FAIL!");
    }
}

public void deleteAll() {
    FileSystemUtils.deleteRecursively(rootLocation.toFile());
}

public void init() {
    try {
        Files.createDirectory(rootLocation);
    } catch (IOException e) {
        throw new RuntimeException("Could not initialize storage!");
    }
}
}

2) Crea Rest Controller per caricare e ottenere il file

@Controller
public class UploadController {

@Autowired
StorageService storageService;

List<String> files = new ArrayList<String>();

@PostMapping("/post")
public ResponseEntity<String> handleFileUpload(@RequestParam("file") MultipartFile file) {
    String message = "";
    try {
        storageService.store(file);
        files.add(file.getOriginalFilename());

        message = "You successfully uploaded " + file.getOriginalFilename() + "!";
        return ResponseEntity.status(HttpStatus.OK).body(message);
    } catch (Exception e) {
        message = "FAIL to upload " + file.getOriginalFilename() + "!";
        return      ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(message);
    }
}

@GetMapping("/getallfiles")
public ResponseEntity<List<String>> getListFiles(Model model) {
    List<String> fileNames = files
            .stream().map(fileName -> MvcUriComponentsBuilder
                    .fromMethodName(UploadController.class, "getFile", fileName).build().toString())
            .collect(Collectors.toList());

    return ResponseEntity.ok().body(fileNames);
}

@GetMapping("/files/{filename:.+}")
@ResponseBody
public ResponseEntity<Resource> getFile(@PathVariable String filename) {
    Resource file = storageService.loadFile(filename);
    return ResponseEntity.ok()
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"")
            .body(file);
}

}

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.