There’s the traditional way of uploading a file using Java servlets. In this post, I’m going to walk through how you can do this with a REST API to keep a clean separation between frontend and backend.
Client-side
This should be familiar html. We set up a form to upload a file like so
<form enctype="multipart/form-data" method="post" action="./rest/upload/"> <div class="form-group"> <label for="uploadfile">File</label> <input type="file" name="uploadfile"> </div> <input type="submit"> </form>
This will make a POST request to “../rest/file” with the file.
Now let’s see how we can do the same with JQuery
$(document).ready(function() { $('#submitJquery').click(submitForm); }); function submitForm() { var file = $('input[name="uploadfile"').get(0).files[0]; var formData = new FormData(); formData.append('uploadfile', file); $.ajax({ url: "../rest/file", type: 'POST', xhr: function() { // Custom XMLHttpRequest var myXhr = $.ajaxSettings.xhr(); return myXhr; }, // beforeSend: beforeSendHandler, success: function(data) { alert('successfully uploaded file with '+data+' lines'); }, // Form data data: formData, //Options to tell jQuery not to process data or worry about content-type. cache: false, contentType: false, processData: false }); }
In the html, make sure you have a submit link to bind to
<a id="submitJquery" href="#">Submit via JQuery</a>
Take care to note the FormData object. And that’s all there is to it.
Now, why would we want to do this? Maybe you want to add additional fields to send. In which case, you can add more key/values by adding to the FormData like so
formData.append('key1','value1'); formData.append('key2','value2'); ...
And here’s how to do this with angularjs
<input type="file" id="csvfile"/> <button ng-click="uploadCSV()">Upload CSV</button>
$scope.uploadCSV = function() { var formData = new FormData(); formData.append('uploadfile', document.getElementById('csvfile').files[0]); $http.post('/my-webapp/rest/csv/upload', formData, { transformRequest: angular.identity, headers: {'Content-Type': undefined} }) }
Server-side
Now, let’s take a look at what needs to be done on the jersey server-side.
import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import com.sun.jersey.core.header.FormDataContentDisposition; import com.sun.jersey.multipart.FormDataParam; @Path ("/file") public class FileResource { @POST @Consumes(MediaType.MULTIPART_FORM_DATA) @Produces(MediaType.TEXT_PLAIN) public Response uploadFile(@FormDataParam("uploadfile") InputStream uploadedInputStream, @FormDataParam("uploadfile") FormDataContentDisposition fileDetail) { try ( BufferedReader reader = new BufferedReader(new InputStreamReader(uploadedInputStream)); ) { int numLines = 0; String line = null; while( (line = reader.readLine()) != null ) { numLines++; System.out.println(line); } return Response.ok(Integer.toString(numLines), "text/plain").build(); } catch (final Exception e) { throw new WebApplicationException(e); } } }
The things to note are the MediaType.MULTIPART_FORM_DATA and the @FormDataParam parameters.
If you wanted to accept the additional “key1” and “key2” values, then just add two more parameters to the method signature like so:
public Response uploadFile(@FormDataParam("uploadfile") InputStream uploadedInputStream, @FormDataParam("uploadfile") FormDataContentDisposition fileDetail, @FormDataParam("key1") String value1, @FormDataParam("key2") String value2) {
Thanks for reading.
Check out this post on how to send and receive arrays of values with the same key name here.