Spring MVC:如何在@ResponseBody中返回图像?

Spring MVC: How to return image in @ResponseBody?

我正在从DB获取图像数据(如byte[])。 如何在@ResponseBody中返回此图像?

编辑

我没有使用HttpServletResponse作为方法参数使用@ResponseBody进行了此操作:

1
2
3
4
5
6
@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());
}

@ResponseBody与注册的org.springframework.http.converter.ByteArrayHttpMessageConverter转换器一起使用为@Sid表示对我不起作用:(。

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

如果您使用的是Spring 3.1或更高版本,则可以在@RequestMapping批注中指定"产生"。下面的示例对我来说开箱即用。如果启用了Web MVC(@EnableWebMvc),则不需要寄存器转换器或其他任何东西。

1
2
3
4
5
6
@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);
}


在Spring 4.1及更高版本中,您可以非常简单地返回几乎所有内容(例如图片,pdf,文档,jar,zip等),而无需任何额外的依赖关系。例如,以下可能是从MongoDB GridFS返回用户的个人资料图片的方法:

1
2
3
4
5
6
7
8
9
10
@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()));
}

注意事项:

  • 以InputStreamResource作为返回类型的ResponseEntity

  • ResponseEntity构建器样式创建

使用此方法,您不必担心HttpServletResponse中的自动装配,抛出IOException或复制流数据。


除了注册ByteArrayHttpMessageConverter,您可能还希望使用ResponseEntity而不是@ResponseBody。以下代码对我有用:

1
2
3
4
5
6
7
8
9
@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);
}

通过使用Spring 3.1.x和3.2.x,这是您应该这样做的方式:

控制器方法:

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

并在servlet-context.xml文件中添加了mvc批注:

1
2
3
4
5
6
7
8
9
10
11
12
<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>

除了几个答案,这里还有一些指针(Spring 4.1)。

如果您在WebMvcConfig中没有配置任何消息转换器,则在@ResponseBody中包含ResponseEntity会很好。

如果这样做,即您使用ResponseEntity配置了一个MappingJackson2HttpMessageConverter(像我一样),则会返回org.springframework.http.converter.HttpMessageNotWritableException

在这种情况下,唯一可行的解??决方案是将byte[]包裹在@ResponseBody中,如下所示:

1
2
3
4
5
@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;
}

在这种情况下,请记住在WebMvcConfig中正确配置消息转换器(并添加ByteArrayHttpMessageConverer),如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@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;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 @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);



}

为我工作。


在您的应用程序上下文中,声明AnnotationMethodHandlerAdapter和registerByteArrayHttpMessageConverter:

1
2
3
4
5
6
7
<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>

还可以在处理程序方法中为您的响应设置适当的内容类型。


我更喜欢这个:

1
2
3
4
5
6
7
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");
}

将媒体类型更改为您拥有的图像格式。


我在春季4上班。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@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();
        }

}

没有答案对我有用,所以我设法做到了:

1
2
3
4
5
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);

设置Content-Disposition标头可以在我的方法中下载带有@ResponseBody批注的文件。


您应该在响应中指定媒体类型。我正在将@GetMapping注释与Produces = MediaType.IMAGE_JPEG_VALUE一起使用。 @RequestMapping将起作用。

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

没有媒体类型,很难猜测实际返回的内容(包括任何阅读代码的人,浏览器,当然还有Spring本身)。 byte []只是不具体。从byte []确定媒体类型的唯一方法是嗅探和猜测。

提供媒体类型只是最佳实践


这就是我使用Spring Boot和Guava的方法:

1
2
3
4
5
@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() );
}

我认为您可能需要一种服务来存储文件上传并获取该文件。
从这里查看更多详细信息

1)创建一个存储服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
@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)创建Rest Controller以上传并获取文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@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);
}

}


在春季4中,您很容易不需要在bean中进行任何更改。仅将返回类型标记为@ResponseBody。

例:-

1
2
3
4
5
6
7
8
@RequestMapping(value ="/image/{id}")
    public @ResponseBody
    byte[] showImage(@PathVariable Integer id) {
                 byte[] b;
        /* Do your logic and return
               */
        return b;
    }