Up- and download files with Java EE and Web Components

Last Updated:  February 26, 2020 | Published: April 13, 2019
In one of my last blog post, I showed you how to upload and download files with React and Spring Boot. Today I want to give you a quickstart example on how to achieve the same with Java EE and Web Components (standards ftw!). In this blog post, I'll be using Java 8, Java EE 8, Payara 5.191 as the application server and no framework for the frontend (except Bootstrap for some styling).

 

The final result will look like the following:

 

 

Setting up the backend with Java EE

As the JAX-RS specification currently doesn't provide a convenient way to work with multipart data (except working with the  HttpServletRequest directly), I'm using some proprietary Jersey code. Similar annotations/methods are available for Apache CXF and RESTEasy as well.
The pom.xml for the backend looks like the following:

The two Jersey dependencies are required for the proprietary annotations, which you'll see in the next step. They are marked as scope provided as they are already packaged within Payara and therefore don't need to pollute the .war file and keep its size thin.
To register the MultipartFeature of Jersey for our JAX-RS endpoints, I'm using the programmatic way like the following:

The ResourceConfig class is part of the jersey-server dependency. You can achieve the same with registering the feature in the web.xml.
With this feature enabled, we can now make use of the @FormDataParam annotation to parse and access the incoming FormData. In addition, Jersey provides metadata of the uploaded file with the class FormDataContentDisposition. As the content type of the incoming request won't be classic application/json, we have to add the correct MediaType to the method:

Within the method I'm converting the incoming InputStream to a byte[] and store it in the embedded H2 database of Payara using this JPA entity:

Besides uploading, I'm also providing an endpoint to download a file. In this example, a random file is retrieved from the database and returned as MediaType.APPLICATION_OCTET_STREAM. In addition, the Content-Disposition header contains the name of the file.

To make the interaction with the frontend work, we have to enable CORS as the browser will otherwise block the request. This is achieved with a JAX-RS @Provider which intercepts the HTTP response and adds custom HTTP headers. Extracting the filename of the downloaded file in the frontend, later on, the header Access-Control-Expose-Headers is important, as we otherwise won't have access to the HTTP header Content-Disposition:

That's everything for the backend.

Setting up the frontend with Web Components

As I'm using no framework for the frontend, the setup is pretty straightforward. In addition to the Bootstrap CSS and JS libraries, I'm adding a custom app.js to the index.html. Within the <body> of the HTML file, you can find a basic page layout and two unknown HTML tags: <upload-component> and <download-component> which are both custom Web Components:

Each component is defined in its own file (DownloadComponent.js and UploadComponent.js) and is really simple for this quickstart (no real configuration from outside to make it reusable), but you should get a good insight into Web Components with them. To show you how you could configure the component from outside, I'm passing the text of the button as a component attribute (caption) from outside:

For creating a Web Component, you have to extend the HTMLElement class and define a custom element with an own HTML tag. Within the constructor of the component, I'm creating the HTML layout of the component with a simple JavaScript string literal (have a look at lit-html for a more advanced way of doing this). In addition, the references to some of the HTML elements are stored as attributes and an onsubmit function is defined for the form.
The file for the upload is wrapped in a FormData object and the Fetch API is used to make the POST HTTP call and upload the file.
The <download-component> is even simpler than the previous one. The component contains just a button and accesses the JAX-RS endpoint for downloading a random file on every click on the button:

Finally, the app.js imports both components so that they are available within the index.html:

You can find the example in my GitHub repository with a docker-compose.yml file for local testing time on your machine.

Have fun up- and downloading files with Web Components and Java EE,
Phil

  • Hello, I have a problem when running the application, Payara shows the error :

    No injection source found for a parameter of type public void entidades.FileUploadResource.uploadFile(java.io.InputStream,org.glassfish.jersey.media.multipart.FormDataContentDisposition) throws java.io.IOException at index 0.; source='ResourceMethod{httpMethod=POST, consumedTypes=[multipart/form-data], producedTypes=[], suspended=false, suspendTimeout=0, suspendTimeoutUnit=MILLISECONDS, invocable=Invocable{handler=ClassBasedMethodHandler{handlerClass=class entidades.FileUploadResource, handlerConstructors=[[email protected]]}, definitionMethod=public void entidades.FileUploadResource.uploadFile(java.io.InputStream,org.glassfish.jersey.media.multipart.FormDataContentDisposition) throws java.io.IOException, parameters=[Parameter [type=class java.io.InputStream, source=file, defaultValue=null], Parameter [type=class org.glassfish.jersey.media.multipart.FormDataContentDisposition, source=file, defaultValue=null]], responseType=void}, nameBindings=[]}']]]

    Thanks for the help.

      • Hello, Thanks
        I work with Payara 5.194, I don’t use maven, I use Apache Netbeans 11.2 with Ant, Java 8 and I add the jars in the same versions as you use in the example.

  • {"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}
    >