I try to upload/stream a large image to a REST controller that takes the file and stores it in to a database.
@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.
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?
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.
In the below examples I store the files in MongoDB GridFS.
Solution 1 - Example after Dirks recommendation
Controller (with curl command for testing in the comment):
/**
*
* @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
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
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.
A new attribute "maxSwallowSize" is the key to deal this situation. It should happen when you upload a file which is larger than 2M. Because the 2M is the default value of this new attribute .
The REST API support was introduced in Spring from version 3.0 onwards; since then, it has steadily evolved to the present day. We can create REST resources in the following ways: Using controllers which are used to handle HTTP requests such as GET, POST, PUT, and so forth.
To pass the Json and Multipart in the POST method we need to mention our content type in the consume part. And we need to pass the given parameter as User and Multipart file. Here, make sure we can pass only String + file not POJO + file. Then convert the String to Json using ObjectMapper in Service layer.
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 ).
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.
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-urlencoded
and multipart/form-data
. Here is for more reading.
Using application/x-www-form-urlencoded
is 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-data
is recommended.
Spring can handle multipart/form-data
content 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.
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/
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.
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.
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.
To retrieve the image, another Spring Controller method can provide the image by streaming the bytes directly.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With