In this tutorial I will explain how to build Java REST web-service to upload files from any client over HTTP.
Uploading files to web-apps is a common task nowadays. A lot of services support uploading pictures or documents on their sites. With Java web services this is easy accomplished. What we need aside form the java web container (provided by your application server like Tomcat, GlassFish or JBoss) is the jersey framework to make it run. First I will show you how to implement the web-service and than give you two examples of clients to use the service.
Building the File Upload REST Service
The file is pushed over HTTP POST with encoding type “multipart/form-data” from the client to our web-service. This way you can add multiple parameters to the POST request in addition to the file. Lets start with the requirements. You will need an web/application server like Tomcat, GlassFish or JBoss to deploy the service. In addition we will use jersey framework to build our service endpoint. Please note, GlassFish 4.x version requires jersey version 2 libraries, so if you are using GlassFish 4 use jersey 2.x dependencies in your POM file instead.
For quick reference you can find the entire project in our GitHub repository under https://github.com/JavaTutorialNetwork/Tutorials/tree/master/FileUploaderRESTService
I will post the entire POM file here, but what you need to take into account are the jersey dependencies
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>net.javatutorial.tutorials</groupId> <artifactId>FileUploaderRESTService</artifactId> <version>1</version> <packaging>war</packaging> <name>File Uploader Rest Service</name> <url>https://javatutorial.net</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-server</artifactId> <version>1.8</version> </dependency> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-servlet</artifactId> <version>1.17.1</version> </dependency> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-json</artifactId> <version>1.8</version> </dependency> <dependency> <groupId>com.sun.jersey.contribs</groupId> <artifactId>jersey-multipart</artifactId> <version>1.8</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <inherited>true</inherited> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> <plugin> <artifactId>maven-war-plugin</artifactId> <version>2.3</version> <configuration> <webXml>src/main/webapp/WEB-INF/web.xml</webXml> <webResources> <resource> <!-- this is relative to the pom.xml directory --> <directory>${project.basedir}/src/main/resources </directory> </resource> </webResources> </configuration> </plugin> </plugins> </build> </project>
Having all necessary libraries now, lets go ahead and implement the REST service. There are several places in the code below I want to point your attention to. First note the usage of @Consumes(MediaType.MULTIPART_FORM_DATA) as requested encoding type. Second you may want to add additional parameters to the method if you like. For example you may want to pass some description or another text data with your upload. Finally Java will throw an Exception if you try to upload a file into a directory which not exists. To avoid this issue I created the method createFolderIfNotExists(String dirName).
package net.javatutorial.tutorials.services; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import com.sun.jersey.core.header.FormDataContentDisposition; import com.sun.jersey.multipart.FormDataParam; /** * This example shows how to build Java REST web-service to upload files * accepting POST requests with encoding type "multipart/form-data". For more * details please read the full tutorial on * https://javatutorial.net/java-file-upload-rest-service * * @author javatutorial.net */ @Path("/upload") public class FileUploadService { /** The path to the folder where we want to store the uploaded files */ private static final String UPLOAD_FOLDER = "c:/uploadedFiles/"; public FileUploadService() { } @Context private UriInfo context; /** * Returns text response to caller containing uploaded file location * * @return error response in case of missing parameters an internal * exception or success response if file has been stored * successfully */ @POST @Consumes(MediaType.MULTIPART_FORM_DATA) public Response uploadFile( @FormDataParam("file") InputStream uploadedInputStream, @FormDataParam("file") FormDataContentDisposition fileDetail) { // check if all form parameters are provided if (uploadedInputStream == null || fileDetail == null) return Response.status(400).entity("Invalid form data").build(); // create our destination folder, if it not exists try { createFolderIfNotExists(UPLOAD_FOLDER); } catch (SecurityException se) { return Response.status(500) .entity("Can not create destination folder on server") .build(); } String uploadedFileLocation = UPLOAD_FOLDER + fileDetail.getFileName(); try { saveToFile(uploadedInputStream, uploadedFileLocation); } catch (IOException e) { return Response.status(500).entity("Can not save file").build(); } return Response.status(200) .entity("File saved to " + uploadedFileLocation).build(); } /** * Utility method to save InputStream data to target location/file * * @param inStream * - InputStream to be saved * @param target * - full path to destination file */ private void saveToFile(InputStream inStream, String target) throws IOException { OutputStream out = null; int read = 0; byte[] bytes = new byte[1024]; out = new FileOutputStream(new File(target)); while ((read = inStream.read(bytes)) != -1) { out.write(bytes, 0, read); } out.flush(); out.close(); } /** * Creates a folder to desired location if it not already exists * * @param dirName * - full path to the folder * @throws SecurityException * - in case you don't have permission to create the folder */ private void createFolderIfNotExists(String dirName) throws SecurityException { File theDir = new File(dirName); if (!theDir.exists()) { theDir.mkdir(); } } }
Finally we need to configure our web.xml to register our class as web-service and make it run on startup.
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>net.javatutorial.tutorials.services</display-name> <servlet> <servlet-name>Jersey REST Service</servlet-name> <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class> <init-param> <param-name>com.sun.jersey.config.property.packages</param-name> <param-value>net.javatutorial.tutorials.services</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Jersey REST Service</servlet-name> <url-pattern>/rest/*</url-pattern> </servlet-mapping> </web-app>
That’s it! Now you can build and deploy the WAR file. If you use the exact same names provided in the code above, your service URL (given you run on localhost) will be: http://localhost:8080/FileUploaderRESTService-1/rest/upload
File Upload HTML Form
You can use a very simple HTML post form as client to send files to the server.
Please note the usage of “multipart/form-data” as encoding type. You also need to add an <input> of type file with the name “file”
Choose file to upload<br> <form action="http://localhost:8080/FileUploaderRESTService-1/rest/upload" method="post" enctype="multipart/form-data"> <input name="file" id="filename" type="file" /><br><br> <button name="submit" type="submit">Upload</button> </form>
As I already mentioned you can add additional data to your request. In this case just don’t forget to handle it in the web-service 🙂
Java File Upload Client
You can create a file upload client for you Android or stand-alone programs in java. I the example below I will use Apache http libraries, you will need this five:
- commons-logging
- httpclient
- httpclient-cache
- httpcore
- httpmime
package net.javatutorial.tutorials.clienst; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.mime.MultipartEntity; import org.apache.http.entity.mime.content.InputStreamBody; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.params.BasicHttpParams; import org.apache.http.util.EntityUtils; /** * This example shows how to upload files using POST requests * with encoding type "multipart/form-data". * For more details please read the full tutorial * on https://javatutorial.net/java-file-upload-rest-service * @author javatutorial.net */ public class FileUploaderClient { public static void main(String[] args) { // the file we want to upload File inFile = new File("C:\\Users\\admin\\Desktop\\Yana-make-up.jpg"); FileInputStream fis = null; try { fis = new FileInputStream(inFile); DefaultHttpClient httpclient = new DefaultHttpClient(new BasicHttpParams()); // server back-end URL HttpPost httppost = new HttpPost("http://localhost:8080/FileUploaderRESTService-1/rest/upload"); MultipartEntity entity = new MultipartEntity(); // set the file input stream and file name as arguments entity.addPart("file", new InputStreamBody(fis, inFile.getName())); httppost.setEntity(entity); // execute the request HttpResponse response = httpclient.execute(httppost); int statusCode = response.getStatusLine().getStatusCode(); HttpEntity responseEntity = response.getEntity(); String responseString = EntityUtils.toString(responseEntity, "UTF-8"); System.out.println("[" + statusCode + "] " + responseString); } catch (ClientProtocolException e) { System.err.println("Unable to make connection"); e.printStackTrace(); } catch (IOException e) { System.err.println("Unable to read file"); e.printStackTrace(); } finally { try { if (fis != null) fis.close(); } catch (IOException e) {} } } }
You will find the project files in our GitHub repository https://github.com/JavaTutorialNetwork/Tutorials/tree/master/FileUploaderJavaClient
Thanks for reading. As always comments are welcome 🙂
Two things: First, if I use I the URL “http://localhost:8080/FileUploaderRESTService-1/rest/upload” that is in the form and the text of the example I get a 404 error. That was from the github version of this. If I take out the “-1” at the end of the service name and use “http://localhost:8080/FileUploaderRESTService/rest/upload” then I got an upload. Is the “-1” just a typo or was it supposed to mean something? Secondly, I started looking at this tutorial because of the use of multipart, as I want to add parameters as part of the upload. Is there somewhere I could find an example… Read more »
Thank you for your comment Sandstones! Good remark with the -1 in the service URL. How your service URL will look like depends on the way you build and deploy the WAR file: 1) If you use the approach right click on Project name -> Run As -> Run on Server , the correct URL of the service will be “http://localhost:8080/FileUploaderRESTService/rest/upload” 2) however if you use maven to build and deploy manually with “mvn clean package” the resulting WAR file will be “FileUploaderRESTService-1.war” . -1 in this case comes from the tag 1 in your POM file and is automatically… Read more »
Thank you for you useful code!
And I have another problem, it’s about pom file because I am in use of Glassfish which have no pom file as a rest web service.
How can I add dependency in Glassfish web service?
I hope you have solution for me!
Many thank!
Hi Sarith,
The POM file is not part of Glassfish server. It is part of your Java Maven project. If you are using Eclipse IDE go to File -> New -> Project… , expand Maven and select “Maven Project”. Follow the steps in the New Maven Project Wizzard. This will create a POM file for you. You can edit this file and add the required dependencies.
Hope this helps!
Hello FILIP!
I am using NetBeans IDE with Glassfish server actually it has only web.xml and no pom.xml! According to your how can I configure it to use the code above or I need to create a new project over the old version instead?
Thank you!
You can import the Eclipse project (found in the GitHub repo : https://github.com/JavaTutorialNetwork/Tutorials/tree/master/FileUploaderRESTService) in NetBeans. Just follow this steps:
1) Download the project
2) In NetBeans , File->New Project->Select maven in left Pane -> Select Project with existing POM on right pane and finish.
3) Now you will be asked to open the downloaded eclipse project
For detailed instruction on how to work with Maven on NetBeans I suggest you read the official NetBeans wiki at http://wiki.netbeans.org/MavenBestPractices
There is an error message of the following instruction after doing it:
Failed to execute goal org.apache.maven.plugins:maven-war-plugin:2.3:war (default-war) on project FileUploaderRESTService: Execution default-war of goal org.apache.maven.plugins:maven-war-plugin:2.3:war failed: basedir C:\Users\nsarith\Downloads\FileUpload\FileUploaderRESTService\src\main\resources does not exist -> [Help 1]
To see the full stack trace of the errors, re-run Maven with the -e switch.
Re-run Maven using the -X switch to enable full debug logging.
For more information about the errors and possible solutions, please read the following articles:
[Help 1] http://cwiki.apache.org/confluence/display/MAVEN/PluginExecutionException
What is it about?
I hope you can help!
Hi Sarith,
The error message you have posted says: “basedir C:\Users\nsarith\Downloads\FileUpload\FileUploaderRESTService\src\main\resources does not exist” – just create the missing “resources” folder and try again (an empty folder should work)
Thank you very much!
For now I have one more issue, it is about FileUploaderClient.
Actually, it worked to post the file with Postman, but doesn’t work with the code above.
May, the problem occur by basic authentication.
Please help me to have the code with basic authentication!
Thank you very much!
Hey Sarith – please check my latest comment on how to implement basic authentication in FileUploaderClient (https://javatutorial.net/java-file-upload-rest-service#comment-137). If that doesn’t work for you, my suggestion is to open a StackOverflow question (with an actual failing test that can be run) – and email me the link. Cheers,
Filip.
Hi Flipp,
The article is amazing. I have few questions. I have already created a Rest web service(URL). Now I only want to call that Api using java. Can I use just the chunck of code you have mentioned above to call that Web service and upload file to a web service?
My requirement is to call a Post method web service and upload entire file to a web service. What code of chunck will be used for that?
Thanks ISHAUKAT!
1) use multipart/form-data encoding for mixed POST content (binary and character data) in your API (REST Web Service). Note the usage of @Consumes(MediaType.MULTIPART_FORM_DATA) annotation in the example above (FileUploadService)
2) Yes. If you need to use Java to upload files to the API you ca use the code in FileUploaderClient (see example above)
Thankyou Flipp.
Does that work for JSON data as well.
I am creating a JSON file from Informatica (ETL) and the file store in ETL server. Now I need to call the web service to upload file from ETL server to an API (web service) using Java.
The issue is I don’t have same format of request when creating JSON file from ETL so do I need a string manipulation and read file from that server when writing code in Java?
There are three options, depending on what you want to do with the content of your ETL file. Option 1 – upload the file to your java server using the code from this tutorial. After uploading you can parse the file and deal with the content Option 2 – you can create a simple POST service and pass the JSON file content as string to it (without the multipart). If your ETL files are large (couple of megabytes or GBs) better go with option 1 @POST @Path("/upload") @Produces(MediaType.APPLICATION_JSON) public String uploadAsString(@Context HttpServletRequest request, @PathParam("json_data") String jsonData) { ... } Option… Read more »
Hi Filip, So this is the modification of code I am using (JavaClient). I am getting this error: [401] This request is not authorized. Even thou I put username & pass of URL: http://localhost:8001/customer (because it had basic authentication). This is my code: package net.javatutorial.tutorials.clienst; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.mime.MultipartEntity; import org.apache.http.entity.mime.content.ContentBody; import org.apache.http.entity.mime.content.InputStreamBody; import org.apache.http.entity.mime.content.StringBody; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.params.BasicHttpParams; import org.apache.http.util.EntityUtils; /** * This example shows how to upload files using POST requests * with encryption type "multipart/form-data". * For more details please read the full… Read more »
If you want to use basic http authentication on your client you can either:
Use a CredentialsPrider
CredentialsProvider provider = new BasicCredentialsProvider();
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials("username", "yourPass");
provider.setCredentials(AuthScope.ANY, credentials);
HttpClient client = HttpClientBuilder.create().setDefaultCredentialsProvider(provider).build();
Please note httpclient version 4.3 + is required
OR
you can set username and pass in the request header like this:
HttpPost httppost = new HttpPost("http://localhost:8080/FileUploaderRESTService-1/rest/upload");
String auth = "username:youpPass";
byte[] encodedAuth = Base64.encodeBase64(auth.getBytes(Charset.forName("ISO-8859-1")));
String authHeader = "Basic " + new String(encodedAuth);
httppost.setHeader(HttpHeaders.AUTHORIZATION, authHeader);
MultipartEntity entity = new MultipartEntity();
when running the client or server of the downloaded project I get error 404 and that the resource is not available
double check your URL. If deployment is successful (no errors in log), the only reason you see the 404 not found is a misspelled URL in the client
There is an error in your method saveToFile in git (it is ok here in the article). The while loop belong to the try block.
However, thank you for the example.
Hi Petr,
I don’t see any differences between the git version of saveToFile method and the one posted in the article. Both version does not have a try – catch block inside saveToFile method. The IOException is thrown from the method and handled in a try-catch inside uploadFile method. Can you please point the exact error so I can fix it?
hello
i’m trying this code on my code but i have this exception:
נוב 26, 2017 4:33:46 PM com.sun.jersey.spi.container.ContainerRequest getEntity
SEVERE: A message body reader for Java class com.sun.jersey.core.header.FormDataContentDisposition, and Java type class com.sun.jersey.core.header.FormDataContentDisposition, and MIME media type multipart/form-data; boundary=—-WebKitFormBoundarykzdzsKUpAFNpL25b was not found.
The registered message body readers compatible with the MIME media type are:
*/* ->
com.sun.jersey.core.impl.provider.entity.FormProvider
com.sun.jersey.core.impl.provider.entity.MimeMultipartProvider
com.sun.jersey.core.impl.provider.entity.StringProvider
com.sun.jersey.core.impl.provider.entity.ByteArrayProvider
com.sun.jersey.core.impl.provider.entity.FileProvider
com.sun.jersey.core.impl.provider.entity.InputStreamProvider
com.sun.jersey.core.impl.provider.entity.DataSourceProvider
com.sun.jersey.core.impl.provider.entity.XMLJAXBElementProvider$General
com.sun.jersey.core.impl.provider.entity.ReaderProvider
com.sun.jersey.core.impl.provider.entity.DocumentProvider
com.sun.jersey.core.impl.provider.entity.SourceProvider$StreamSourceReader
com.sun.jersey.core.impl.provider.entity.SourceProvider$SAXSourceReader
com.sun.jersey.core.impl.provider.entity.SourceProvider$DOMSourceReader
com.sun.jersey.json.impl.provider.entity.JSONJAXBElementProvider$General
com.sun.jersey.json.impl.provider.entity.JSONArrayProvider$General
com.sun.jersey.json.impl.provider.entity.JSONObjectProvider$General
com.sun.jersey.core.impl.provider.entity.XMLRootElementProvider$General
com.sun.jersey.core.impl.provider.entity.XMLListElementProvider$General
com.sun.jersey.core.impl.provider.entity.XMLRootObjectProvider$General
com.sun.jersey.core.impl.provider.entity.EntityHolderReader
com.sun.jersey.json.impl.provider.entity.JSONRootElementProvider$General
com.sun.jersey.json.impl.provider.entity.JSONListElementProvider$General
com.sun.jersey.json.impl.provider.entity.JacksonProviderProxy
please help
Thank you! I’m just getting started with Restful web services and this is absolutely the best illustration and example I’ve seen. This covers everything essentually. I need to write data files to the cloud and this is the best. thank you Filip
Thank you Gary for your kind words!
Filip, thank you for sharing your wealth of knowledge, I am truly grateful. beginners … I wish there was a way to thank you! Filip, I have a pending question I am struggling with. How do you authenticate on an Cloud env, given UUID and such? like Amazon, Azure, or … what is the magic here? it seems the auth method may be different? again, thank you very much!
Hey Gary, thank you very much for your kind words! This is what keeps me motivated writing my tutorials. If you manage your own application server like Glassfish, JBoss etc. on the cloud, for example EC2 instance on AWS you do not need any special magic to authenticate and you can go with this tutorial alone to upload your files. However most cloud based solution offer their own file storage like S3 on AWS. If you want to use S3 for file storage instead I have written an extended tutorial on this topic too. You can find it here: https://javatutorial.net/java-s3-example… Read more »
Thanks a lot man, was able to solve this multipart form data problem using your sample code. Already spent 2 hours on it.
Hi Filip, Great article.
However I’m getting “405 Method Not Allowed” as an error. When I ran the Firefox RESTCleint I see that the only methods allowed are GET,OPTIONS.
Any input on how to go about fixing this.
Thanks
Hi, are you sure you have marked the uploadFile(…) method with the @POST annotation?
Hi HttpClient have been abandoned and what is its solution
Good morning
Thanks for the excellent guide, it is really very helpful!
I have one question:
How do I handle the additional info that I want to be sent along with the image?
For instance I want to send input field ‘name’ along with the image.
How do I handle that in the web service?
Thank you in advance!
Best
J.
Hi, filip
This tutorials is so useful!
i am using apache tomcat 8 with your uploaded code. but, i hava a problem with jersey dependency.
when i run JavaHttpClient i get this exception: HTTP Status 500 – Servlet.init() for servlet Jersey REST Service threw exception. it takes a lot more of my time but i can’t. So, please help me, to solve this issue. Thank you!
Now no problem. i have solved my problem. I just forgot to add path above my HttpRestServer class name. hurrrrey! what a complicated problem. that i have spended a day for the solution.
org.glassfish.jersey.server.model.ModelValidationException: is throwns
Hi
This code helps me to use file as input to a post ws call. but i get response code as 202. also using the file upload code , the response string i receive is null? what needs to be done here