1. 概述

本文将演示如何使用Spring MVC框架返回图像及其他多媒体资源。我们将探讨多种实现方式,从直接操作HttpServletResponse开始,逐步过渡到利用消息转换内容协商和Spring的Resource抽象的高级方案。每种方案都将深入分析其优缺点。

2. 直接操作HttpServletResponse

最基础的图像下载方式是直接操作response对象,模拟原生Servlet实现。代码示例如下:

@RequestMapping(value = "/image-manual-response", method = RequestMethod.GET)
public void getImageAsByteArray(HttpServletResponse response) throws IOException {
    InputStream in = servletContext.getResourceAsStream("/WEB-INF/images/image-example.jpg");
    response.setContentType(MediaType.IMAGE_JPEG_VALUE);
    IOUtils.copy(in, response.getOutputStream());
}

浏览器访问以下URL即可显示图像:

http://localhost:8080/spring-mvc-xml/image-manual-response.jpg

优点:实现简单直接,依赖org.apache.commons.ioIOUtils工具类。
缺点:扩展性差,MIME类型硬编码,修改图像存储位置或转换逻辑需改动代码。更灵活的方案见下节。

3. 使用HttpMessageConverter

前述方案未利用Spring MVC的消息转换内容协商特性。启用这些特性需:

  • 在控制器方法添加@ResponseBody注解
  • 根据返回类型注册合适的消息转换器(如处理byte[]ByteArrayHttpMessageConverter

3.1. 配置转换器

以内置的ByteArrayHttpMessageConverter为例(默认已注册,但可显式配置):

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>

Java配置

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    converters.add(byteArrayHttpMessageConverter());
}

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

3.2. 实现方法

控制器方法返回byte[]并添加@ResponseBody注解:

@RequestMapping(value = "/image-byte-array", method = RequestMethod.GET)
public @ResponseBody byte[] getImageAsByteArray() throws IOException {
    InputStream in = servletContext.getResourceAsStream("/WEB-INF/images/image-example.jpg");
    return IOUtils.toByteArray(in);
}

测试URL:

http://localhost:8080/spring-mvc-xml/image-byte-array.jpg

优点

  • 方法与HttpServletResponse解耦
  • 转换过程高度可配置
  • 响应类型通过URL后缀(如.jpg)自动协商,无需硬编码

缺点

  • 需手动实现数据源(本地文件/外部存储)读取逻辑
  • 无法控制响应头和状态码

4. 使用ResponseEntity

通过ResponseEntity<byte[]>可同时控制响应体、头部和状态码:

@RequestMapping(value = "/image-response-entity", method = RequestMethod.GET)
public ResponseEntity<byte[]> getImageAsResponseEntity() {
    HttpHeaders headers = new HttpHeaders();
    InputStream in = servletContext.getResourceAsStream("/WEB-INF/images/image-example.jpg");
    byte[] media = IOUtils.toByteArray(in);
    headers.setCacheControl(CacheControl.noCache().getHeaderValue());
    
    ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(media, headers, HttpStatus.OK);
    return responseEntity;
}

核心优势

  • 精确控制状态码:异常场景(如文件未找到)可直接返回404
    catch (FileNotFoundException e) {
        return new ResponseEntity<>(null, headers, HttpStatus.NOT_FOUND);
    }
    
  • 简化头部设置:比操作HttpServletResponse更直观,方法签名更清晰

5. 使用Resource类返回图像

将图像抽象为Resource对象,利用Spring的统一资源访问机制:

5.1. 实现方案

基础版本

@ResponseBody
@RequestMapping(value = "/image-resource", method = RequestMethod.GET)
public Resource getImageAsResource() {
   return new ServletContextResource(servletContext, "/WEB-INF/images/image-example.jpg");
}

增强版本(控制头部)

@RequestMapping(value = "/image-resource", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<Resource> getImageAsResource() {
    HttpHeaders headers = new HttpHeaders();
    Resource resource = 
      new ServletContextResource(servletContext, "/WEB-INF/images/image-example.jpg");
    return new ResponseEntity<>(resource, headers, HttpStatus.OK);
}

核心优势

  • 解耦资源位置:通过ResourceLoader动态加载资源(本地文件/远程文件/类路径)
  • 配置化管理:图像存储路径可外部化配置,无需修改代码
  • 消除样板代码:无需手动实现文件读取逻辑

6. 总结

本文对比了四种图像返回方案:

  1. **直接操作HttpServletResponse**:简单但僵硬,适合快速原型
  2. **HttpMessageConverter**:利用消息转换和内容协商,提升灵活性
  3. **ResponseEntity**:精细控制响应状态和头部,适合复杂场景
  4. Resource抽象:最高灵活性,统一资源访问,推荐生产环境使用

提示:Spring Boot用户可参考《使用Spring下载图像或文件》获取更现代的实现方案。完整示例代码见GitHub仓库


原始标题:Returning Image/Media Data with Spring MVC

« 上一篇: Spring WebSockets 介绍