我如何以一种宁静的方式使用 Spring 3.2 spring-mvc 上传/流式传输大图像
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14615692/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do i upload/stream large images using Spring 3.2 spring-mvc in a restful way
提问by Christopher Armstrong
I try to upload/stream a large image to a REST controller that takes the file and stores it in to a database.
我尝试将大图像上传/流式传输到 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
}
}
This is a non-working example of what I'm trying to achieve (of course, or I guess so InputStream is not working). I want to stream/read the image over the @RequestBody.
这是我试图实现的一个非工作示例(当然,或者我猜所以 InputStream 不起作用)。我想通过@RequestBody 流式传输/读取图像。
I have searched everywhere but can't find a good example how to achieve this. Most people seem to ask only how to upload images over forms but don't use REST/RestTemplate to do it. Is there anyone that can help me with this?
我到处搜索,但找不到如何实现这一目标的好例子。大多数人似乎只问如何通过表单上传图像,但不使用 REST/RestTemplate 来做到这一点。有没有人可以帮我解决这个问题?
I'm thankful for any hint in to the right direction.
我很感谢任何关于正确方向的提示。
Kind regards, Chris
亲切的问候,克里斯
Solutions
解决方案
Below here I try to post the solutions that worked for me after the Input from Dirk and Gigadot. At the moment I think both solutions are worth while looking at. At first I try to post a working example with the help I got from Dirk and then I'll try to create one with the help from Gigadot. I will mark Dirks answer as the correct one as I have been asking explicitly how to upload the file over the @RequestBody. But I'm also curious to test the solution from Gigadot as it is maybe easier and more common to use.
在下面,我尝试在 Dirk 和 Gigadot 的输入之后发布对我有用的解决方案。目前我认为这两种解决方案都值得研究。起初,我尝试在 Dirk 的帮助下发布一个工作示例,然后我将尝试在 Gigadot 的帮助下创建一个。我将 Dirks 的答案标记为正确答案,因为我一直在明确询问如何通过 @RequestBody 上传文件。但我也很想测试 Gigadot 的解决方案,因为它可能更容易和更常用。
In the below examples I store the files in MongoDB GridFS.
在下面的示例中,我将文件存储在 MongoDB GridFS 中。
Solution 1 - Example after Dirks recommendation
解决方案 1 - Dirks 推荐后的示例
Controller (with curl command for testing in the comment):
控制器(在评论中使用 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 (unfinished but working as a test):
服务(未完成但作为测试工作):
@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;
}
}
Solution 2 - Example after Gigadots recommendation
解决方案 2 - Gigadots 推荐后的示例
This is described in the Spring manual: http://static.springsource.org/spring/docs/3.2.1.RELEASE/spring-framework-reference/html/mvc.html#mvc-multipart
这在 Spring 手册中有描述:http: //static.springsource.org/spring/docs/3.2.1.RELEASE/spring-framework-reference/html/mvc.html#mvc-multipart
This is quite easy and also contains all information by default. I think I'll go for this solution at least for the binary uploads.
这很容易,并且默认情况下还包含所有信息。我想我至少会为二进制上传选择这个解决方案。
Thanks everyone for posting and for your answers. It's much appreciated.
感谢大家发帖和回答。非常感谢。
采纳答案by Dirk Lachowski
As it looks as if you are using spring you could use HttpEntity ( http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/http/HttpEntity.html).
看起来好像您正在使用 spring,您可以使用 HttpEntity ( http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/http/HttpEntity.html)。
Using it, you get something like this (look at the 'payload' thing):
使用它,你会得到这样的东西(看看“有效载荷”):
@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.");
}
}
We're using PUT here because it's a RESTfull "put an image to a product". 'spn' is the products number, the imagename is created by fileService.store(). Of course you could also POST the image to create the image resource.
我们在这里使用 PUT 是因为它是一个 RESTfull“将图像放入产品”。'spn' 是产品编号,图像名称由 fileService.store() 创建。当然你也可以 POST 图像来创建图像资源。
回答by gigadot
When you send a POST request, there are two type of encoding you can use to send the form/parameters/files to the server, i.e. application/x-www-form-urlencodedand multipart/form-data. Here is for more reading.
当您发送POST请求,有两种类型的编码就可以使用到窗体/参数/文件发送到服务器,即application/x-www-form-urlencoded和multipart/form-data。这是更多阅读。
Using application/x-www-form-urlencodedis not a good idea if you have a large content in your POST request because it usually crashes the web browser (from my experience). Thus,
multipart/form-datais recommended.
application/x-www-form-urlencoded如果 POST 请求中有大量内容,则使用不是一个好主意,因为它通常会导致 Web 浏览器崩溃(根据我的经验)。因此,
multipart/form-data被推荐。
Spring can handle multipart/form-datacontent automatically if you add a multipart resolver to your dispatcher. Spring's implementation for handling this is done using apache-commons-fileupload so you will need to add this library to your project.
multipart/form-data如果您向调度程序添加多部分解析器,Spring 可以自动处理内容。Spring 用于处理此问题的实现是使用 apache-commons-fileupload 完成的,因此您需要将此库添加到您的项目中。
Now for the main answer of how to actually do it is already been blogged here http://viralpatel.net/blogs/spring-mvc-multiple-file-upload-example/
现在,关于如何实际操作的主要答案已经在博客http://viralpatel.net/blogs/spring-mvc-multiple-file-upload-example/
I hope that might help you to find a solution. You may want to read up about what REST is. It sounds like you are a bit confused. Basically, almost all http requests are RESTful even if the urls are ugly.
我希望这可以帮助您找到解决方案。您可能想了解 REST 是什么。听起来你有点糊涂了。基本上,即使 url 很难看,几乎所有的 http 请求都是 RESTful。
回答by Jay
I have done this recently, see the details here.
On the browser we can make use of the file API to load a file, then encode its contents using Base64 encoding, and finally assign the encoded contents to a javascript object before posting it.
在浏览器上,我们可以使用文件 API 来加载文件,然后使用 Base64 编码对其内容进行编码,最后在发布之前将编码的内容分配给一个 javascript 对象。
On the server, we have a Spring Controller which will handle the request. As part of the json unmarshalling that converts the the request body to a java object, the base64-encoded value for the image bytes will be converted to a standard Java byte[], and stored as a LOB in the database.
在服务器上,我们有一个 Spring 控制器来处理请求。作为将请求正文转换为 java 对象的 json 解组的一部分,图像字节的 base64 编码值将转换为标准 Java byte[],并作为 LOB 存储在数据库中。
To retrieve the image, another Spring Controller method can provide the image by streaming the bytes directly.
为了检索图像,另一个 Spring Controller 方法可以通过直接流式传输字节来提供图像。
The blog post I linked to above presumes you want to use the image as part of another object, but the principle should be the same if you want to only work with the image directly. Let me know if anything needs clarification.
我上面链接的博客文章假定您想将图像用作另一个对象的一部分,但如果您只想直接使用图像,则原理应该相同。如果有什么需要澄清的,请告诉我。

