Spring MVC: Wie kann ich ein Bild in @ResponseBody zurückgeben?

142

Ich erhalte Bilddaten (as byte[]) von der DB. Wie kann ich dieses Bild zurückgeben @ResponseBody?

BEARBEITEN

Ich habe es ohne @ResponseBodyVerwendung HttpServletResponseals Methodenparameter:

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

Die Verwendung @ResponseBodymit einem registrierten org.springframework.http.converter.ByteArrayHttpMessageConverterKonverter, wie @Sid sagte, funktioniert bei mir nicht :(.

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

Antworten:

97

Wenn Sie die Spring-Version 3.1 oder neuer verwenden, können Sie in der @RequestMappingAnnotation "Erzeugnisse" angeben . Das folgende Beispiel funktioniert für mich sofort. Sie benötigen keinen Registerkonverter oder etwas anderes, wenn Sie Web MVC aktiviert haben ( @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
quelle
77

Mit Spring 4.1 und höher können Sie so ziemlich alles (wie Bilder, PDFs, Dokumente, Gläser, Reißverschlüsse usw.) ganz einfach ohne zusätzliche Abhängigkeiten zurückgeben. Das Folgende könnte beispielsweise eine Methode sein, um das Profilbild eines Benutzers aus MongoDB GridFS zurückzugeben:

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

Die Dinge zu beachten:

  • ResponseEntity mit InputStreamResource als Rückgabetyp

  • Erstellung des ResponseEntity Builder-Stils

Mit dieser Methode müssen Sie sich keine Gedanken über das automatische Verdrahten in der HttpServletResponse, das Auslösen einer IOException oder das Kopieren von Streamdaten machen.

Jaymes Bearden
quelle
1
Dies löst die folgende Ausnahme aus: Wie serialisieren Sie MyInputStream?: Inhalt konnte nicht geschrieben werden: Für die Klasse com.mongodb.gridfs.GridFSDBFile $ MyInputStream
Nestor Ledon
Während dies hauptsächlich als Beispiel dafür dient, was Sie möglicherweise tun könnten, gibt Mongo-Java-Driver 3.0.3 mit GridFsDBFile.getInputStream () keine anonyme Klasse namens MyInputStream zurück. Ich würde deine Versionen überprüfen - vielleicht aktualisieren?
Jaymes Bearden
4
Mir gefällt, wie dies die Datei streamt, anstatt das Ganze in den Speicher zu kopieren. Siehe auch stackoverflow.com/questions/20333394/…
Wim Deblauwe
60

Zusätzlich zur Registrierung von a ByteArrayHttpMessageConvertermöchten Sie möglicherweise a ResponseEntityanstelle von verwenden @ResponseBody. Der folgende Code funktioniert für mich:

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

Mit Spring 3.1.x und 3.2.x sollten Sie Folgendes tun:

Die Controller-Methode:

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

Und die mvc-Annotation in der Datei 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
quelle
13

Neben ein paar Antworten hier ein paar Hinweise (Spring 4.1).

Incase Sie haben noch keine messageconverters in Ihrem WebMvcConfig konfiguriert, mit ResponseEntityin Ihrem @ResponseBodygut funktioniert.

Wenn Sie dies tun, dh Sie haben eine MappingJackson2HttpMessageConverter(wie ich) mit den ResponseEntityRückgaben a konfiguriert org.springframework.http.converter.HttpMessageNotWritableException.

Die einzige funktionierende Lösung ist in diesem Fall eine wickeln byte[]in der @ResponseBodywie folgt:

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

Denken Sie in diesem Fall daran, die Nachrichtenkonverter ordnungsgemäß ByteArrayHttpMessageConvererin Ihrer WebMvcConfig zu konfigurieren (und ein a hinzuzufügen ).

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

Deklarieren Sie in Ihrem Anwendungskontext einen AnnotationMethodHandlerAdapter und einen 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> 

Stellen Sie auch in der Handler-Methode den entsprechenden Inhaltstyp für Ihre Antwort ein.

Sid
quelle
@jsinghfoss beziehen sich auf die Top-Antwort.
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);



}

Arbeitete für mich.

Vivex
quelle
5

Ich bevorzuge diesen:

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

Ändern Sie den Medientyp in ein beliebiges Bildformat.

Tarion
quelle
1
Guter Aufruf ResourceLoader, aber das Erstellen
Qerub
3

Es ist Arbeit für mich im Frühjahr 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
quelle
2

Keine der Antworten hat bei mir funktioniert, also habe ich es so gemacht:

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

Einstellen Content-DispositionHeader konnte ich die Datei mit der zum Download @ResponseBodyAnmerkung auf meiner Methode.

Paulius Matulionis
quelle
1

So mache ich es mit Spring Boot und 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
quelle
1

Sie sollten den Medientyp in der Antwort angeben. Ich verwende eine @ GetMapping-Annotation mit generate = MediaType.IMAGE_JPEG_VALUE. @RequestMapping funktioniert genauso.

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

Ohne einen Medientyp ist es schwer zu erraten, was tatsächlich zurückgegeben wird (einschließlich aller Personen, die den Code, den Browser und natürlich Spring selbst lesen). Ein Byte [] ist einfach nicht spezifisch. Die einzige Möglichkeit, den Medientyp aus einem Byte [] zu bestimmen, besteht darin, herumzuschnüffeln und zu raten.

Die Bereitstellung eines Medientyps ist nur eine bewährte Methode

coseos
quelle
Es funktioniert für mich in Spring Boot 2.x. Ich danke Ihnen für das Teilen.
Attacomsian
0

Im Frühjahr 4 ist es sehr einfach, dass Sie keine Änderungen an den Bohnen vornehmen müssen. Markieren Sie Ihren Rückgabetyp nur für @ResponseBody.

Beispiel:-

@RequestMapping(value = "/image/{id}")
    public @ResponseBody
    byte[] showImage(@PathVariable Integer id) {
                 byte[] b;
        /* Do your logic and return 
               */
        return b;
    }
Ankit Katiyar
quelle
1
Das Problem dabei ist, dass der Inhaltstyp nicht richtig eingestellt ist.
ETL
0

Ich denke, Sie brauchen vielleicht einen Dienst, um das Hochladen von Dateien zu speichern und diese Datei zu erhalten. Überprüfen Sie mehr Details von hier

1) Erstellen Sie einen Speicherservice

@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) Erstellen Sie einen Rest Controller zum Hochladen und Abrufen der Datei

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

}}

Langer Nguyen
quelle