【问题标题】:Spring MVC: How to return image in @ResponseBody?Spring MVC:如何在@ResponseBody 中返回图像?
【发布时间】:2023-03-29 13:46:01
【问题描述】:

我正在从 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);
}

【问题讨论】:

    标签: spring-mvc


    【解决方案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 的匿名类。我会检查你的版本——也许更新?
    • 我喜欢这种流式传输文件的方式,而不是将整个内容复制到内存中。另见stackoverflow.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");
          }
          

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

          【讨论】:

          【解决方案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无法转换它并引发异常:找不到转换器。

                    【讨论】:

                      猜你喜欢
                      • 2023-03-19
                      • 2011-06-18
                      • 2013-03-08
                      • 2014-10-22
                      • 2015-04-26
                      • 1970-01-01
                      • 2012-09-02
                      • 1970-01-01
                      • 2016-02-29
                      相关资源
                      最近更新 更多