Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do i upload/stream large images using Spring 3.2 spring-mvc in a restful way

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.

like image 945
Christopher Armstrong Avatar asked Jan 30 '13 23:01

Christopher Armstrong


People also ask

How would you handle the situation where a user uploads a very large file through a form in your Spring MVC application?

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 .

Can we make REST API using Spring MVC?

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.

How do you upload an image or file along with the post JSON data Spring Boot?

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.


3 Answers

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.

like image 156
Dirk Lachowski Avatar answered Sep 24 '22 11:09

Dirk Lachowski


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.

like image 45
gigadot Avatar answered Sep 24 '22 11:09

gigadot


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.

like image 35
Jason Avatar answered Sep 20 '22 11:09

Jason