Multipart requests in action

In the preceding exercise, you saw how to incorporate a static View to show product images on the product details page. We simply put some images in a directory on the server and did some configuration, and Spring MVC was able to pick up those files while rendering the product details page. What if we automated this process? I mean, instead of putting those images in the directory, what if we were able to upload the images to the image directory?

How can we do this? Here comes the multipart request. A multipart request is a type of HTTP request to send files and data to the server. Spring MVC provides good support for multipart requests. Let's say we want to upload some files to the server, then we have to form a multipart request to accomplish that.

Time for action - adding images to a product

Let's add an image upload facility in our add products page:

  1. Add a bean definition in our web application context configuration file (WebApplicationContextConfig.java) for CommonsMultipartResolver as follows:
          @Bean 
          public CommonsMultipartResolver multipartResolver() { 
              CommonsMultipartResolver resolver=new       
          CommonsMultipartResolver(); 
              resolver.setDefaultEncoding("utf-8"); 
              return resolver; 
          } 
    
  2. Open pom.xml, which you can find under the project root directory itself.
  3. You will be able to see some tabs under pom.xml; select the Dependencies tab and click on the Add button of the Dependencies section.
  4. A Select Dependency window will appear; in Group Id enter commons-fileupload, in Artifact Id enter commons-fileupload, in Version enter 1.2.2, select Scope as compile, then click on the OK button.
  5. Similarly, add one more dependency: org.apache.commons as Group Id, commons-io as Artifact Id, 1.3.2 as Version, and Scope as compile, then click on the OK button and save pom.xml.
  6. Open our product's domain class (product.java) and add a reference to org.springframework.web.multipart.MultipartFile with corresponding setters and getters as follows (don't forget to add getters and setters for this field):
          private MultipartFile productImage; 
    
  7. Open addProduct.jsp, which you can find under the/src/main/webapp/WEB-INF/views/ directory in your project, and add the following set of tags after the <form:input id="condition"> tag group:
          <div class="form-group"> 
             <label class="control-label col-lg-2" for="productImage">       
             <spring:message code="addProduct.form.productImage.label"/>       </label> 
             <div class="col-lg-10"> 
                <form:input id="productImage" path="productImage" 
    
          type="file" class="form:input-large" /> 
             </div> 
          </div> 
    
  8. Add an entry in our message bundle source (messages.properties) for the product's image label, as follows:
          addProduct.form.productImage.label = Product Image file 
    
  9. Now set the enctype attribute to multipart/form-data in the form tag as follows and save addProduct.jsp:
          <form:form  modelAttribute="newProduct" class="form-
          horizontal" enctype="multipart/form-data"> 
    
  10. Open our ProductController.java and modify the processAddNewProductForm method's signature by adding an extra method parameter of the type HttpServletRequest (javax.servlet.http.HttpServletRequest); so basically your processAddNewProductForm method signature should look like the following code snippet:
          public String processAddNewProductForm(       
          @ModelAttribute("newProduct") Product newProduct,       
          BindingResult result, HttpServletRequest request) { 
    
  11. Add the following code snippet inside the processAddNewProductForm method just before productService.addProduct(newProduct):
          MultipartFile productImage = newProduct.getProductImage(); 
          String rootDirectory = 
          request.getSession().getServletContext().getRealPath("/"); 
           
             if (productImage!=null && !productImage.isEmpty()) { 
                 try { 
                   productImage.transferTo(new 
          File(rootDirectory+"resources\images"+ 
          newProduct.getProductId() + ".png")); 
                 } catch (Exception e) { 
                throw new RuntimeException("Product Image saving 
          failed", e); 
             } 
             } 
    
  12. Within the initialiseBinder method, add a productImage field to the whitelisting set as follows:
          binder.setAllowedFields("productId", 
                      "name", 
                      "unitPrice", 
                      "description", 
                      "manufacturer", 
                      "category", 
                      "unitsInStock", 
                      "condition", 
                      "productImage"); 
    
  13. Now run our application and enter the URL http://localhost:8080/webstore/market/products/add. You will be able to see our add products page with an extra input field so you can choose which file to upload. Just fill out all the information as usual and, importantly, pick an image file of your choice for the newly-added image file; click on the Add button. You will be able to see that the image has been added to the Products page and to the product details page.
    Time for action - adding images to a product

    The add products page with the image selection option

What just happened?

Spring's CommonsMultipartResolver (org.springframework.web.multipart.commons.CommonsMultipartResolver) class is the thing that determines whether the given request contains multipart content and parses the given HTTP request into multipart files and parameters. That's the reason we created a bean for that class within our web application context in step 1. And, through the setMaxUploadSize property, we set a maximum of 10,240,000 bytes as the allowed file size to be uploaded:

@Bean 
public CommonsMultipartResolver multipartResolver() { 
    CommonsMultipartResolver resolver=new CommonsMultipartResolver(); 
    resolver.setDefaultEncoding("utf-8"); 
    resolver.setMaxUploadSize(10240000); 
    return resolver; 
} 

From steps 2 to 5, we added some of the org.apache.commons libraries as our Maven dependencies. This is because Spring uses those libraries internally to support the file uploading feature.

Since the image that we were uploading belongs to a product, it is better to keep that image as part of the product information; that's why in step 6 we added a reference to the MultipartFile in our domain class (Product.java) and added corresponding setters and getters. This MultipartFile reference holds the actual product image file that we are uploading.

We want to incorporate the image uploading facility in our add products page; that's why, in the addProduct.jsp View file, we added a file input tag to choose the desired image:

<div class="form-group"> 
<label class="control-label col-lg-2" for="productImage"> <spring:message code="addProduct.form.productImage.label"/> 
</label> 
   <div class="col-lg-10"> 
      <form:input id="productImage" path="productImage" type="file" class="form:input-large" /> 
   </div> 
</div> 

In the preceding set of tags, the important one is the <form:input> tag, which has the type attribute as file so that it can make the Choose File button display the file chooser window. As usual, we want this form field to be bound with the domain object field; that's the reason we gave the path attribute as productImage. If you remember, this path name is just the same MultipartFile reference name that we added in step 6.

As usual, we want to externalize the label message for this file input tag as well; that's why we added <spring:message>, and in step 8 we added the corresponding message entry in the message source file (messages.properties).

Since our add product form is now capable of sending an image file as well as part of the request, we need to encode the request as a multipart request. This is why in step 9 we added the enctype attribute to the <form:form> tag and set its value as multipart/form-data. The enctype attribute indicates how the form data should be encoded when submitting it to the server.

We wanted to save the image file in the server under the resources/images directory, as this directory structure will be available directly under the root directory of our web application at runtime. So, in order to get the root directory of our web application, we need HttpServletRequest. See the following code snippet:

String rootDirectory = request.getSession().getServletContext().getRealPath("/"); 

That's the reason we added an extra method parameter called request of the type HttpServletRequest to our processAddNewProductForm method in step 10. Remember, Spring will fill this request parameter with the actual HTTP request.

In step 11, we simply read the image file from the domain object and wrote it into a new file with the product ID as the name:

MultipartFile productImage = newProduct.getProductImage(); 
String rootDirectory = request.getSession().getServletContext().getRealPath("/"); 
       
   if (!productImage.isEmpty()) { 
       try { 
         productImage.transferTo(new File(rootDirectory+"resources\images"+newProduct.getProductId() + ".png")); 
       } catch (Exception e) { 
      throw new RuntimeException("Product Image saving failed", e); 
   } 
   } 

Remember, we purposely saved the images with the product ID name because we have already designed our products (products.jsp) page and details (product.jsp) page accordingly to show the right image based on the product ID.

And as a final step, we added the newly introduced productImage file to the whitelisting set in the binder configuration within the initialiseBinder method.

Now if you run your application and enter http://localhost:8080/webstore/market/products/add, you will be able to see your add products page with an extra input field to choose the file to upload.

Have a go hero - uploading product user manuals to the server

It's nice that we were able to upload the product image to the server while adding a new product. Why don't you extend this facility to upload a PDF file to the server? For example, consider that every product has a user manual and you want to upload these user manuals while adding a product.

Here are some of the things you can do to upload PDF files:

  • Create a directory with the name pdf under the src/main/webapp/resources/ directory in your project
  • Add one more MultipartFile reference in your product domain class (Product.java) to hold the PDF file and change Product.java accordingly
  • Extend addProduct.jsp
  • Extend ProductController.java accordingly; don't forget to add the newly added field to the whitelist

So finally, if the newly added product ID is P1237, you will be able to access the PDF under http://localhost:8080/webstore/pdf/P1237.pdf.

Good luck!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.22.60.167