Spring MVC: ¿Cómo devolver la imagen en @ResponseBody?

142

Estoy obteniendo datos de imagen (como byte[]) de DB. ¿Cómo devolver esta imagen @ResponseBody?

EDITAR

Lo hice sin @ResponseBodyusarHttpServletResponse como parámetro de método:

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

Usar @ResponseBodycon el org.springframework.http.converter.ByteArrayHttpMessageConverterconvertidor registrado como @Sid dijo que no funciona para mí :(.

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

Respuestas:

97

si está utilizando la versión Spring de 3.1 o posterior, puede especificar "produce" en la @RequestMappinganotación. El siguiente ejemplo me funciona fuera de la caja. No necesita convertidor de registro ni nada más si tiene web mvc habilitado ( @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
fuente
78

Con Spring 4.1 y superior, puede devolver casi cualquier cosa (como imágenes, archivos PDF, documentos, tarros, cremalleras, etc.) de manera bastante simple sin ninguna dependencia adicional. Por ejemplo, lo siguiente podría ser un método para devolver una imagen de perfil de usuario 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()));
}

Las cosas a tener en cuenta:

  • ResponseEntity con InputStreamResource como tipo de retorno

  • Creación de estilo de generador de ResponseEntity

Con este método, no tiene que preocuparse por el cableado automático en HttpServletResponse, lanzar una IOException o copiar los datos de la transmisión.

Jaymes Bearden
fuente
1
Esto arroja la siguiente excepción, ¿cómo está serializando MyInputStream ?: No se pudo escribir el contenido: No se encontró un serializador para la clase com.mongodb.gridfs.GridFSDBFile $ MyInputStream
Nestor Ledon
Si bien esto es principalmente un ejemplo de lo que podría hacer, Mongo-Java-Driver 3.0.3 con GridFsDBFile.getInputStream () no devuelve una clase anónima llamada MyInputStream. Verificaría sus versiones, ¿quizás actualizar?
Jaymes Bearden
44
Me gusta cómo esto transmite el archivo en lugar de copiar todo en la memoria. Ver también stackoverflow.com/questions/20333394/…
Wim Deblauwe
60

Además de registrar un ByteArrayHttpMessageConverter, es posible que desee utilizar un en ResponseEntitylugar de @ResponseBody. El siguiente código funciona para mí:

@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
fuente
16

Al usar Spring 3.1.xy 3.2.x, así es como debe hacerlo:

El método del controlador:

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

Y la anotación mvc en el archivo 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
fuente
13

Además de algunas respuestas aquí, algunos consejos (Spring 4.1).

En caso de que no tenga ningún convertidor de mensajes configurado en su WebMvcConfig, tenerlo ResponseEntitydentro @ResponseBodyfunciona bien.

Si lo hace, es decir, tiene un MappingJackson2HttpMessageConverterconfigurado (como yo) utilizando las ResponseEntitydevoluciones a org.springframework.http.converter.HttpMessageNotWritableException.

La única solución de trabajo en este caso es envolver un byte[]en el @ResponseBodysiguiente:

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

En este caso, recuerde configurar los convertidores de mensajes correctamente (y agregue un ByteArrayHttpMessageConverer) en su WebMvcConfig, así:

@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
fuente
6

En el contexto de su aplicación, declare un AnnotationMethodHandlerAdapter y 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> 

también en el método del controlador establezca el tipo de contenido apropiado para su respuesta.

Sid
fuente
@jsinghfoss se refieren a la respuesta principal.
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);



}

Trabajó para mi.

Vivex
fuente
5

Prefiero este:

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

Cambie el tipo de medio a cualquier formato de imagen que tenga.

Tarion
fuente
1
Buena llamada en ResourceLoader, pero la construcción de una ruta de entrada externa como en el ejemplo es una mala idea: cwe.mitre.org/data/definitions/22.html
qerub
3

Es trabajo para mí en la 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();
        }

}
Furetto
fuente
2

Ninguna de las respuestas funcionó para mí, así que me las arreglé para hacerlo así:

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

Configurando el Content-Dispositionencabezado pude descargar el archivo con la @ResponseBodyanotación en mi método.

Paulius Matulionis
fuente
2

Debe especificar el tipo de medio en la respuesta. Estoy usando una anotación @GetMapping con produce = MediaType.IMAGE_JPEG_VALUE. @RequestMapping funcionará igual.

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

Sin un tipo de medio, es difícil adivinar lo que realmente se devuelve (incluye a cualquiera que lea el código, el navegador y, por supuesto, el propio Spring). Un byte [] simplemente no es específico. La única forma de determinar el tipo de medio a partir de un byte [] es husmear y adivinar.

Proporcionar un tipo de medio es la mejor práctica

coseos
fuente
A mí me funciona en Spring Boot 2.x. Gracias por compartir.
attacomsian
1

Así es como lo hago con Spring Boot y 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
fuente
0

En la primavera 4 es muy fácil, no necesita hacer ningún cambio en los frijoles. Solo marque su tipo de devolución en @ResponseBody.

Ejemplo:-

@RequestMapping(value = "/image/{id}")
    public @ResponseBody
    byte[] showImage(@PathVariable Integer id) {
                 byte[] b;
        /* Do your logic and return 
               */
        return b;
    }
Ankit Katiyar
fuente
1
El problema que tengo con esto es que el tipo de contenido no está configurado correctamente.
ETL
0

Creo que quizás necesite un servicio para almacenar la carga de archivos y obtener ese archivo. Consulta más detalles desde aquí

1) Crear un servicio de almacenamiento

@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) Crear controlador de descanso para cargar y obtener el archivo

@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
fuente