Spring MVC: Comment retourner l'image dans @ResponseBody?

142

Je reçois des données d'image (comme byte[] ) de DB. Comment renvoyer cette image @ResponseBody?

ÉDITER

Je l'ai fait sans @ResponseBodyutiliser HttpServletResponsecomme paramètre de méthode:

@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());
}

Utiliser @ResponseBodyavec un org.springframework.http.converter.ByteArrayHttpMessageConverterconvertisseur enregistré comme @Sid l'a dit ne fonctionne pas pour moi :(.

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

Réponses:

97

si vous utilisez la version Spring de 3.1 ou plus récente, vous pouvez spécifier "produit" dans l' @RequestMappingannotation. L'exemple ci-dessous fonctionne pour moi hors de la boîte. Pas besoin de convertisseur de registre ou quoi que ce soit d'autre si vous avez activé le 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);
}
michal.kreuzman
la source
78

Avec Spring 4.1 et au-dessus, vous pouvez retourner à peu près tout (comme des images, des fichiers PDF, des documents, des jars, des zips, etc.) tout simplement sans aucune dépendance supplémentaire. Par exemple, ce qui suit pourrait être une méthode pour renvoyer l'image de profil d'un utilisateur à partir de 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()));
}

Les choses à noter:

  • ResponseEntity avec InputStreamResource comme type de retour

  • Création de style de générateur ResponseEntity

Avec cette méthode, vous n'avez pas à vous soucier du câblage automatique dans HttpServletResponse, du lancement d'une IOException ou de la copie des données de flux.

Jaymes Bearden
la source
1
Cela lève l'exception suivante, comment sérialisez-vous MyInputStream?: Impossible d'écrire le contenu: aucun sérialiseur trouvé pour la classe com.mongodb.gridfs.GridFSDBFile $ MyInputStream
Nestor Ledon
Bien qu'il s'agisse principalement d'un exemple de ce que vous pourriez faire, Mongo-Java-Driver 3.0.3 avec GridFsDBFile.getInputStream () ne renvoie pas une classe anonyme appelée MyInputStream. Je vérifierais vos versions - peut-être mettre à jour?
Jaymes Bearden
4
J'aime la façon dont cela diffuse le fichier au lieu de copier le tout en mémoire. Voir aussi stackoverflow.com/questions/20333394/…
Wim Deblauwe
60

En plus d'enregistrer un ByteArrayHttpMessageConverter, vous pouvez utiliser un à la ResponseEntityplace de @ResponseBody. Le code suivant fonctionne pour moi:

@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);
}
Siggen
la source
16

En utilisant Spring 3.1.x et 3.2.x, voici comment procéder:

La méthode du contrôleur:

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

Et l'annotation mvc dans le fichier 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>
Peymankh
la source
13

En plus de quelques réponses, voici quelques conseils (Spring 4.1).

Dans le cas où vous n'avez aucun convertisseur de messagerie configuré dans votre WebMvcConfig, ayant à l' ResponseEntityintérieur de votre@ResponseBody travaux bien.

Si vous le faites, c'est à dire que vous avez MappingJackson2HttpMessageConverterconfiguré (comme moi) en utilisant les ResponseEntityretours aorg.springframework.http.converter.HttpMessageNotWritableException .

La seule solution de travail dans ce cas est d'envelopper un byte[]dans le @ResponseBodycomme suit:

@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;
}

Dans ce cas, n'oubliez pas de configurer correctement les convertisseurs de messages (et ajoutez un ByteArrayHttpMessageConverer) dans votre WebMvcConfig, comme ceci:

@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;
}
s.ijpma
la source
6

Dans le contexte de votre application, déclarez un AnnotationMethodHandlerAdapter et 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> 

également dans la méthode du gestionnaire, définissez le type de contenu approprié pour votre réponse.

Sid
la source
@jsinghfoss se réfère à la première réponse.
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);



}

A travaillé pour moi.

Vivex
la source
5

Je préfère celui-ci:

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");
}

Changez le type de support pour le format d'image que vous avez.

Tarion
la source
1
Bon appel ResourceLoader, mais construire un chemin à partir d'une entrée externe comme dans votre exemple est une mauvaise idée: cwe.mitre.org/data/definitions/22.html
qerub
3

Cela fonctionne pour moi au printemps 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();
        }

}
Furetto
la source
2

Aucune des réponses n'a fonctionné pour moi, alors j'ai réussi à le faire comme ça:

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);

Définition de l'en- Content-Dispositiontête J'ai pu télécharger le fichier avec l' @ResponseBodyannotation sur ma méthode.

Paulius Matulionis
la source
2

Vous devez spécifier le type de média dans la réponse. J'utilise une annotation @GetMapping avec produit = MediaType.IMAGE_JPEG_VALUE. @RequestMapping fonctionnera de la même manière.

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

Sans un type de média, il est difficile de deviner ce qui est réellement retourné (y compris quiconque lit le code, le navigateur et bien sûr Spring lui-même). Un octet [] n'est tout simplement pas spécifique. La seule façon de déterminer le type de média à partir d'un octet [] est de renifler et de deviner.

Fournir un type de média n'est que la meilleure pratique

Coseos
la source
Cela fonctionne pour moi dans Spring Boot 2.x. Merci pour le partage.
attacomsian le
1

Voici comment je le fais avec Spring Boot et 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() );
}
Wim Deblauwe
la source
0

Au printemps 4, il est très facile de ne pas modifier les haricots. Marquez uniquement votre type de retour sur @ResponseBody.

Exemple:-

@RequestMapping(value = "/image/{id}")
    public @ResponseBody
    byte[] showImage(@PathVariable Integer id) {
                 byte[] b;
        /* Do your logic and return 
               */
        return b;
    }
Ankit Katiyar
la source
1
Le problème que j'obtiens avec ceci est que le type de contenu n'est pas défini correctement.
ETL
0

Je pense que vous avez peut-être besoin d'un service pour stocker le téléchargement de fichiers et obtenir ce fichier. Vérifiez plus de détails sur ici

1) Créer un service de stockage

@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) Créez un contrôleur de repos pour télécharger et obtenir un fichier

@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);
}

}

Long Nguyen
la source