Spring MVC:如何在@ResponseBody 中返回图像?
Posted
技术标签:
【中文标题】Spring MVC:如何在@ResponseBody 中返回图像?【英文标题】:Spring MVC: How to return image in @ResponseBody? 【发布时间】:2011-08-07 02:36:11 【问题描述】:我正在从 DB 获取图像数据(如 byte[]
)。如何在@ResponseBody
中返回这张图片?
编辑
我在没有@ResponseBody
的情况下使用HttpServletResponse
作为方法参数:
@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 所说的对我不起作用:(。
@ResponseBody
@RequestMapping("/photo2")
public byte[] testphoto() throws IOException
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
return IOUtils.toByteArray(in);
【问题讨论】:
【参考方案1】:如果您使用的是 Spring 3.1 或更高版本,您可以在 @RequestMapping
注释中指定“produces”。下面的示例对我来说是开箱即用的。如果您启用了 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);
【讨论】:
这种方式有没有办法指定文件名?【参考方案2】:使用 Spring 4.1 及更高版本,您可以非常简单地返回几乎任何内容(例如图片、pdf、文档、jar、zip 等),而无需任何额外的依赖。例如,以下可能是从 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()));
注意事项:
以 InputStreamResource 作为返回类型的 ResponseEntity
ResponseEntity 构建器样式创建
使用此方法,您不必担心 HttpServletResponse 中的自动装配、抛出 IOException 或复制流数据。
【讨论】:
这会引发以下异常,您如何序列化 MyInputStream?:无法写入内容:找不到类 com.mongodb.gridfs.GridFSDBFile$MyInputStream 的序列化程序 虽然这主要是作为您可以做的一个示例,但带有 GridFsDBFile.getInputStream() 的 Mongo-Java-Driver 3.0.3 不会返回名为 MyInputStream 的匿名类。我会检查你的版本——也许更新? 我喜欢这种流式传输文件的方式,而不是将整个内容复制到内存中。另见***.com/questions/20333394/…【参考方案3】:除了注册ByteArrayHttpMessageConverter
,您可能还想使用ResponseEntity
而不是@ResponseBody
。以下代码适用于我:
@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);
【讨论】:
【参考方案4】:通过使用 Spring 3.1.x 和 3.2.x,你应该这样做:
控制器方法:
@RequestMapping("/photo2")
public @ResponseBody byte[] testphoto() throws IOException
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
return IOUtils.toByteArray(in);
以及servlet-context.xml文件中的mvc注解:
<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>
【讨论】:
【参考方案5】:除了几个答案之外,还有一些提示(Spring 4.1)。
如果您没有在 WebMvcConfig 中配置任何消息转换器,则在您的 @ResponseBody
中包含 ResponseEntity
效果很好。
如果你这样做了,即你有一个 MappingJackson2HttpMessageConverter
配置(像我一样)使用 ResponseEntity
返回一个 org.springframework.http.converter.HttpMessageNotWritableException
。
在这种情况下,唯一可行的解决方案是将byte[]
包装在@ResponseBody
中,如下所示:
@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
),如下所示:
@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】:我更喜欢这个:
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");
将媒体类型更改为您拥有的任何图像格式。
【讨论】:
对ResourceLoader
的良好调用,但如您的示例中那样从外部输入构造路径名是一个坏主意:cwe.mitre.org/data/definitions/22.html【参考方案7】:
在您的应用程序上下文中声明一个 AnnotationMethodHandlerAdapter 和 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>
还在处理程序方法中为您的响应设置适当的内容类型。
【讨论】:
@jsinghfoss 参考最佳答案。【参考方案8】: @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);
为我工作。
【讨论】:
【参考方案9】:您应该在响应中指定媒体类型。我正在使用带有produce = MediaType.IMAGE_JPEG_VALUE 的@GetMapping 注释。 @RequestMapping 也一样。
@GetMapping(value="/current/chart",produces = MediaType.IMAGE_JPEG_VALUE)
@ResponseBody
public byte[] getChart()
return ...;
没有媒体类型,很难猜测实际返回的内容(包括阅读代码的任何人、浏览器,当然还有 Spring 本身)。 byte[] 只是不具体。从 byte[] 确定媒体类型的唯一方法是嗅探和猜测。
提供媒体类型只是最佳实践
【讨论】:
它适用于我在 Spring Boot 2.x 中。谢谢分享。【参考方案10】:春季 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();
【讨论】:
【参考方案11】:没有一个答案对我有用,所以我设法做到了:
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
注释的文件。
【讨论】:
【参考方案12】:这就是我使用 Spring Boot 和 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() );
【讨论】:
【参考方案13】:在 spring 4 中,您无需对 bean 进行任何更改非常容易。仅将您的返回类型标记为 @ResponseBody。
示例:-
@RequestMapping(value = "/image/id")
public @ResponseBody
byte[] showImage(@PathVariable Integer id)
byte[] b;
/* Do your logic and return
*/
return b;
【讨论】:
我遇到的问题是内容类型设置不正确。【参考方案14】:我认为您可能需要一项服务来存储文件上传并获取该文件。 查看here的更多详细信息
1) 创建存储服务
@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 上传和获取文件
@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);
【讨论】:
【参考方案15】:当使用带有 MediaType.IMAGE_JPEG_VALUE 的产品时,请确保您返回的是 byte[],而不是 Byte[]。很奇怪,但是spring无法转换它并引发异常:找不到转换器。
【讨论】:
以上是关于Spring MVC:如何在@ResponseBody 中返回图像?的主要内容,如果未能解决你的问题,请参考以下文章
如何将 Spring Boot 项目迁移到旧 Spring MVC 项目。面临的问题,如何在遗留 Spring MVC 项目中读取 application.properties 文件