Spring MVC: Bagaimana cara mengembalikan gambar di @ResponseBody?

142

Saya mendapatkan data gambar (as byte[]) dari DB. Bagaimana cara mengembalikan gambar ini @ResponseBody?

EDIT

Saya melakukannya tanpa @ResponseBodymenggunakan HttpServletResponseparameter metode:

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

Menggunakan @ResponseBodydengan org.springframework.http.converter.ByteArrayHttpMessageConverterkonverter terdaftar seperti yang dikatakan @id tidak bekerja untuk saya :(.

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

Jawaban:

97

jika Anda menggunakan versi Spring 3.1 atau yang lebih baru, Anda dapat menentukan "menghasilkan" dalam @RequestMappinganotasi. Contoh di bawah ini berfungsi untuk saya di luar kotak. Tidak perlu mendaftar konverter atau apa pun jika Anda mengaktifkan 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
sumber
78

Dengan Spring 4.1 dan di atasnya, Anda dapat mengembalikan apa saja (seperti gambar, pdf, dokumen, guci, ritsleting, dll) dengan cukup sederhana tanpa ada ketergantungan tambahan. Misalnya, berikut ini bisa menjadi metode untuk mengembalikan gambar profil pengguna dari 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()));
}

Hal-hal yang perlu diperhatikan:

  • ResponseEntity dengan InputStreamResource sebagai tipe pengembalian

  • Pembuatan gaya builder ResponseEntity

Dengan metode ini Anda tidak perlu khawatir tentang autowiring di HttpServletResponse, melempar IOException atau menyalin data aliran.

Jaymes Bearden
sumber
1
Ini melempar pengecualian berikut, bagaimana Anda membuat serialisasi MyInputStream ?: Tidak dapat menulis konten: Tidak ada serializer ditemukan untuk kelas com.mongodb.gridfs.GridFSDBFile $ MyInputStream
Nestor Ledon
Meskipun ini terutama sebagai contoh dari apa yang dapat Anda lakukan, Mongo-Java-Driver 3.0.3 dengan GridFsDBFile.getInputStream () tidak mengembalikan kelas anonim yang disebut MyInputStream. Saya akan memeriksa versi Anda - mungkin pembaruan?
Jaymes Bearden
4
Saya suka bagaimana ini mengalirkan file daripada menyalin semuanya dalam memori. Lihat juga stackoverflow.com/questions/20333394/...
Wim Deblauwe
60

Selain mendaftar ByteArrayHttpMessageConverter, Anda mungkin ingin menggunakan ResponseEntitybukan @ResponseBody. Kode berikut ini berfungsi untuk saya:

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

Dengan menggunakan Spring 3.1.x dan 3.2.x, ini adalah bagaimana Anda harus melakukannya:

Metode pengontrol:

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

Dan anotasi mvc dalam 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>
Peymankh
sumber
13

Selain beberapa jawaban di sini, beberapa petunjuk (Musim Semi 4.1).

Jika Anda tidak memiliki messageconverters yang dikonfigurasi di WebMvcConfig Anda, memiliki ResponseEntitydi dalam @ResponseBodypekerjaan Anda dengan baik.

Jika Anda melakukannya, yaitu Anda telah MappingJackson2HttpMessageConvertermengkonfigurasi (seperti saya) menggunakan ResponseEntitypengembalian a org.springframework.http.converter.HttpMessageNotWritableException.

Satu-satunya solusi yang berfungsi dalam hal ini adalah untuk membungkus byte[]dalam @ResponseBodysebagai berikut:

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

Dalam hal ini lakukan rememeber untuk mengkonfigurasi messageconverters dengan benar (dan tambahkan a ByteArrayHttpMessageConverer) di WebMvcConfig Anda, seperti:

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

Dalam konteks aplikasi Anda, nyatakan AnnotationMethodHandlerAdapter dan daftarkanByteArrayHttpMessageConverter:

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

juga dalam metode penanganan mengatur jenis konten yang sesuai untuk respons Anda.

Sid
sumber
@ jsinghfoss merujuk pada jawaban teratas.
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);



}

Bekerja untukku.

Vivex
sumber
5

Saya lebih suka yang ini:

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

Ubah jenis media menjadi format gambar apa pun yang Anda miliki.

Tarion
sumber
1
Panggilan bagus ResourceLoader, tetapi membuat nama path dari input eksternal seperti pada contoh Anda adalah ide yang buruk: cwe.mitre.org/data/definitions/22.html
qerub
3

Ini bekerja untuk saya di Spring 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
sumber
2

Tidak ada jawaban yang bekerja untuk saya, jadi saya berhasil melakukannya seperti itu:

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

Pengaturan Content-Dispositiontajuk Saya dapat mengunduh file dengan @ResponseBodyanotasi pada metode saya.

Paulius Matulionis
sumber
2

Anda harus menentukan jenis media dalam respons. Saya menggunakan anotasi @GetMapping dengan menghasilkan = MediaType.IMAGE_JPEG_VALUE. @RequestMapping akan berfungsi sama.

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

Tanpa jenis media, sulit untuk menebak apa yang dikembalikan (termasuk siapa saja yang membaca kode, peramban, dan tentu saja Spring itu sendiri). Byte [] tidak spesifik. Satu-satunya cara untuk menentukan jenis media dari byte [] adalah mengendus dan menebak-nebak.

Menyediakan jenis media adalah praktik terbaik

coseos
sumber
Ini bekerja untuk saya di Spring Boot 2.x. Terima kasih sudah berbagi.
attacomsian
1

Ini adalah bagaimana saya melakukannya dengan Boot Musim Semi dan Jambu Biji:

@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
sumber
0

Pada musim semi 4 sangat mudah Anda tidak perlu melakukan perubahan pada kacang. Tandai saja tipe pengembalian Anda ke @ResponseBody.

Contoh:-

@RequestMapping(value = "/image/{id}")
    public @ResponseBody
    byte[] showImage(@PathVariable Integer id) {
                 byte[] b;
        /* Do your logic and return 
               */
        return b;
    }
Ankit Katiyar
sumber
1
Masalah yang saya dapatkan dengan ini adalah jenis konten tidak diatur dengan benar.
ETL
0

Saya pikir Anda mungkin perlu layanan untuk menyimpan unggahan file dan mendapatkan file itu. Lihat lebih detail dari sini

1) Buat Layanan Penyimpanan

@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) Buat Rest Controller untuk mengunggah dan mendapatkan 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);
}

}

Long Nguyen
sumber