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

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

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.


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

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>

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

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.


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


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.


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

}

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.


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


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

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

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

}

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.