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

我正在从数据库中获取图像数据(作为 byte[])。如何返回这个图像在 @ResponseBody

剪辑

我没有使用 HttpServletResponse作为方法参数而使用 @ResponseBody:

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

像@Sid 说的那样使用带有注册 org.springframework.http.converter.ByteArrayHttpMessageConverter转换器的 @ResponseBody对我来说不起作用:。

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

在应用程序上下文中声明 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>

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

除了注册 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);
}

通过使用 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>

如果您使用的是 Spring 3.1或更新的版本,您可以在 @RequestMapping注释中指定“ products”。下面的例子对我来说是开箱即用的。不需要寄存器转换器或其他任何东西,如果你有网络 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);
}

在春天,你很容易就不需要对豆子做任何改变。只将返回类型标记为@ResponseBody。

例子:-

@RequestMapping(value = "/image/{id}")
public @ResponseBody
byte[] showImage(@PathVariable Integer id) {
byte[] b;
/* Do your logic and return
*/
return b;
}

这是我用 Spring Boot 和番石榴做的:

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

使用 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 或复制流数据。

没有一个答案对我有用,所以我设法这样做了:

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注释在我的方法。

这是我在春季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();
}


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






}

对我有用。

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

如果在 WebMvcConfig 中没有配置任何消息转换器,那么在 @ResponseBody中配置 ResponseEntity可以很好地工作。

如果你这样做,也就是说,你有一个 MappingJackson2HttpMessageConverter配置(像我一样)使用的 ResponseEntity返回一个 org.springframework.http.converter.HttpMessageNotWritableException

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

@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 中正确配置 message 转换器(并添加 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;
}

我更喜欢这个:

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

将媒体类型更改为所有图像格式。

我认为你可能需要一个服务来存储文件上传和获得该文件。 点击这里查看更多细节

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)创建休息控制器上传和获取文件

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

}

您应该在响应中指定媒体类型。我使用的是@GetMapping 注释,其中包含 products = MediaType.IMAGE _ JPEG _ VALUE。@ RequestMapping 的工作原理是一样的。

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

如果没有媒体类型,就很难猜测实际返回的是什么(包括读取代码的任何人、浏览器,当然还有 Spring 本身)。字节[]不是特定的。根据 byte []确定媒体类型的唯一方法是四处嗅探和猜测。

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

当与 MediaType.IMAGE _ JPEG _ VALUE 一起使用 products 时,请确保返回的是 Byte [] ,而不是 Byte []。非常奇怪,但是 Spring 无法转换它,并引发了一个异常: 没有找到转换器。