如何使用Spring 3.2 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

这非常容易,并且默认情况下还包含所有信息。我认为我至少会针对二进制上传使用此解决方案。

感谢大家的张贴和您的回答。非常感谢。


问题答案:

看起来好像您在使用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”是产品编号,图像名称由fileService.store()创建。当然,您也可以发布图像以创建图像资源。