我如何以一种宁静的方式使用Spring 3.2 spring-mvc上传/流式传输大图像

时间:2013-01-30 23:18:12

标签: spring rest spring-mvc

我尝试将大图像上传/流式传输到REST控制器,该控制器接收文件并将其存储到数据库中。

@Controller
@RequestMapping("/api/member/picture")
public class MemberPictureResourceController {

  @RequestMapping(value = "", method = RequestMethod.POST)
  @ResponseStatus(HttpStatus.NO_CONTENT)
  public void addMemberPictureResource(@RequestBody InputStream image) {
    // Process and Store image in database
  }
}

这是我正在努力实现的一个非工作示例(当然,或者我猜这样,InputStream不起作用)。我想通过@RequestBody流式传输/读取图像。

我到处搜索但找不到如何实现这一目标的好例子。大多数人似乎只询问如何通过表单上传图像,但不使用REST / RestTemplate来执行此操作。有没有人可以帮我解决这个问题?

我很感谢任何正确方向的暗示。

亲切的问候, 克里斯

解决方案

下面我尝试在Dirk和Gigadot的输入后发布适用于我的解决方案。目前我认为两种解决方案都值得一看。起初我尝试在Dirk的帮助下发布一个工作示例,然后我将尝试在Gigadot的帮助下创建一个。我会将Dirks的答案标记为正确答案,因为我一直在明确询问如何通过@RequestBody上传文件。但我也很想测试Gigadot的解决方案,因为它可能更容易,更常见。

在下面的示例中,我将文件存储在MongoDB GridFS中。

解决方案1 ​​ - 推荐Dirks后的示例

控制器(在评论中使用curl命令进行测试):

/**
*
* @author charms
* curl -v -H "Content-Type:image/jpeg" -X PUT --data-binary @star.jpg http://localhost:8080/api/cardprovider/logo/12345
*/
@Controller
@RequestMapping("/api/cardprovider/logo/{cardprovider_id}")
public class CardproviderLogoResourceController {

  @Resource(name = "cardproviderLogoService")
  private CardproviderLogoService cardproviderLogoService;

  @RequestMapping(value = "", method = RequestMethod.PUT)
  @ResponseStatus(HttpStatus.NO_CONTENT)
  public void addCardproviderLogo(@PathVariable("cardprovider_id") String cardprovider_id,
      HttpEntity<byte[]> requestEntity) {
    byte[] payload = requestEntity.getBody();
    InputStream logo = new ByteArrayInputStream(payload);
    HttpHeaders headers = requestEntity.getHeaders();

    BasicDBObject metadata = new BasicDBObject();
    metadata.put("cardproviderId", cardprovider_id);
    metadata.put("contentType", headers.getContentType().toString());
    metadata.put("dirShortcut", "cardproviderLogo");
    metadata.put("filePath", "/resources/images/cardproviders/logos/"); 

    cardproviderLogoService.create1(logo, metadata);
  }
}

服务(未完成但作为测试):

@Service
public class CardproviderLogoService {

  @Autowired
  GridFsOperations gridOperation;

  public Boolean create1(InputStream content, BasicDBObject metadata) {
    Boolean save_state = false;
    try {
      gridOperation.store(content, "demo.jpg", metadata);
      save_state = true;
    } catch (Exception ex) {
      Logger.getLogger(CardproviderLogoService.class.getName())
          .log(Level.SEVERE, "Storage of Logo failed!", ex);
    }
    return save_state;    
  }
}

解决方案2 - Gigadots推荐之后的示例

Spring手册中对此进行了描述: http://static.springsource.org/spring/docs/3.2.1.RELEASE/spring-framework-reference/html/mvc.html#mvc-multipart

这很简单,默认情况下也包含所有信息。我想我至少会为二进制文件上传这个解决方案。

感谢大家发帖和回答。非常感谢。

4 个答案:

答案 0 :(得分:10)

因为看起来你正在使用spring,你可以使用HttpEntity(http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/http/HttpEntity.html)。

使用它,你得到这样的东西(看看'有效载荷'的东西):

@Controller
public class ImageServerEndpoint extends AbstractEndpoint {

@Autowired private ImageMetadataFactory metaDataFactory;
@Autowired private FileService fileService;

@RequestMapping(value="/product/{spn}/image", method=RequestMethod.PUT) 
public ModelAndView handleImageUpload(
        @PathVariable("spn") String spn,
        HttpEntity<byte[]> requestEntity, 
        HttpServletResponse response) throws IOException {
    byte[] payload = requestEntity.getBody();
    HttpHeaders headers = requestEntity.getHeaders();

    try {
        ProductImageMetadata metaData = metaDataFactory.newSpnInstance(spn, headers);
        fileService.store(metaData, payload);
        response.setStatus(HttpStatus.NO_CONTENT.value());
        return null;
    } catch (IOException ex) {
        return internalServerError(response);
    } catch (IllegalArgumentException ex) {
        return badRequest(response, "Content-Type missing or unknown.");
    }
}

我们在这里使用PUT,因为它是一个RESTfull“将图像放到产品上”。 'spn'是产品编号,imagename由fileService.store()创建。当然,您也可以POST图像以创建图像资源。

答案 1 :(得分:6)

发送POST请求时,可以使用两种类型的编码将表单/参数/文件发送到服务器,即application/x-www-form-urlencodedmultipart/form-data。这是更多reading

如果您的POST请求中包含大量内容,则使用application/x-www-form-urlencoded并不是一个好主意,因为它通常会导致Web浏览器崩溃(根据我的经验)。从而, 建议multipart/form-data

如果向调度程序添加多部分解析程序,Spring可以自动处理multipart/form-data内容。 Spring的处理实现是使用apache-commons-fileupload完成的,因此您需要将此库添加到项目中。

现在,有关如何实际执行此操作的主要答案已经在此处进行了博客http://viralpatel.net/blogs/spring-mvc-multiple-file-upload-example/

我希望这可以帮助您找到解决方案。您可能想要了解REST是什么。听起来你有点困惑。基本上,即使网址很难看,几乎所有的http请求都是RESTful。

答案 2 :(得分:4)

我最近这样做了,请参阅details here

在浏览器上,我们可以使用文件API加载文件,然后使用Base64编码对其内容进行编码,最后在发布之前将编码内容分配给javascript对象。

在服务器上,我们有一个Spring Controller来处理请求。作为将请求体转换为java对象的json unmarshalling的一部分,image字节的base64编码值将转换为标准Java byte [],并作为LOB存储在数据库中。

要检索图像,另一个Spring Controller方法可以通过直接传输字节来提供图像。

我上面链接的博客文章假设您想要将图像用作另一个对象的一部分,但如果您只想直接使用图像,原则应该相同。如果有任何需要澄清,请告诉我。

答案 3 :(得分:0)

如果您希望将请求正文作为InputStream处理,则可以直接从Request中获取它。

@Controller
@RequestMapping("/api/member/picture")
public class MemberPictureResourceController {

    @RequestMapping(value = "", method = RequestMethod.POST)
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void addMemberPictureResource(HttpServletRequest request) {
        try {
            InputStream is = request.getInputStream();
            // Process and Store image in database
        } catch (IOException e) {
            // handle exception
        }
    }
}