In my latest blog post, I demonstrated a solution for up- and downloading files with Jersey (JAX-RS 2.1) on Payara. As WildFly does not rely on Jersey as the JAX-RS reference implementation and is using RESTEasy instead, I'll show you a quick example for file handling with RESTEasy on WildFly.
Setting up the backend using RESTEasy
For this blog post, I'm using a classic Java EE 8 Maven application with RESTEasy's multipart-provider as a provided
and additional dependency (no need to package the library within the .war
as WildFly already contains this lib):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <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>de.rieckpil.blog</groupId> <artifactId>rest-easy-file-uploading-and-downloading</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>8.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-multipart-provider</artifactId> <version>3.6.3.Final</version> <scope>provided</scope> </dependency> </dependencies> <build> <finalName>rest-easy-file-uploading-and-downloading</finalName> </build> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <failOnMissingWebXml>false</failOnMissingWebXml> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> </project> |
The JAX-RS configuration is pretty straightforward:
1 2 3 4 | @ApplicationPath("resources") public class JAXRSConfiguration extends Application { } |
Next, for storing the uploaded file, I'm using a simple JPA entity called FileUpload
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | @Entity public class FileUpload { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String fileName; private String contentType; @Lob private byte[] data; // constructors, getters & setters } |
The file handling within the JAX-RS endpoint method is not as straightforward as with Jersey as you have to work with the MultipartFormDataInput
interface and manually extract the filename and content-type:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | @Path("files") @Stateless public class FileUploadResource { @PersistenceContext private EntityManager em; @POST @Consumes(MediaType.MULTIPART_FORM_DATA) public void uploadFile(MultipartFormDataInput incomingFile) throws IOException { InputPart inputPart = incomingFile.getFormDataMap().get("file").get(0); InputStream uploadedInputStream = inputPart.getBody(InputStream.class, null); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; while ((len = uploadedInputStream.read(buffer)) != -1) { byteArrayOutputStream.write(buffer, 0, len); } FileUpload upload = new FileUpload( getFileNameOfUploadedFile(inputPart.getHeaders().getFirst("Content-Disposition")), getContentTypeOfUploadedFile(inputPart.getHeaders().getFirst("Content-Type")), byteArrayOutputStream.toByteArray()); em.persist(upload); } } |
RESTEasy file handling: extract the filename
For extracting the filename I wrote a small helper method which parses the HTTP header Content-Disposition
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | /** * Parses the HTTP Content-Disposition header to extract the filename. If the * header is missing or not containing a filename attribute, 'unknown' is * returned. * * Sample HTTP Content-Disposition header: * * Content-Disposition=[form/data;filename="foo.txt"] * * @param contentDispositionHeader * @return the name of the uploaded file */ private String getFileNameOfUploadedFile(String contentDispositionHeader) { if (contentDispositionHeader == null || contentDispositionHeader.isEmpty()) { return "unkown"; } else { String[] contentDispositionHeaderTokens = contentDispositionHeader.split(";"); for (String contentDispositionHeaderToken : contentDispositionHeaderTokens) { if ((contentDispositionHeaderToken.trim().startsWith("filename"))) { return contentDispositionHeaderToken.split("=")[1].trim().replaceAll("\"", ""); } } return "unkown"; } } |
Similarly, extracting the content type of the file is even simpler:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /** * Parses the HTTP Content-Type header to extract the type (e.g. image/jpeg) of * the file. If the header is missing or empty, 'unknown' is returned. * * Sample HTTP Content-Type header: * * Content-Type=[image/jpeg] * * @param contentTypeHeader * @return the type of the uploaded file */ private String getContentTypeOfUploadedFile(String contentTypeHeader) { if (contentTypeHeader == null || contentTypeHeader.isEmpty()) { return "unkown"; } else { return contentTypeHeader.replace("[", "").replace("]", ""); } } |
The required code for downloading a file does not depend on any proprietary RESTEasy and is just JAX-RS and Java EE standard (in the example I'm offering a random file for the user to download):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | @GET @Produces(MediaType.APPLICATION_OCTET_STREAM) public Response getRandomFile() { Long amountOfFiles = em.createQuery("SELECT COUNT(f) FROM FileUpload f", Long.class).getSingleResult(); Long randomPrimaryKey; if (amountOfFiles == null || amountOfFiles == 0) { return Response.ok().build(); } else if (amountOfFiles == 1) { randomPrimaryKey = 1 L; } else { randomPrimaryKey = ThreadLocalRandom.current().nextLong(1, amountOfFiles + 1); } FileUpload randomFile = em.find(FileUpload.class, randomPrimaryKey); return Response.ok(randomFile.getData(), MediaType.APPLICATION_OCTET_STREAM) .header("Content-Disposition", "attachment; filename=" + randomFile.getFileName()).build(); } |
You'll find the full code base on GitHub with an HTML page to upload files and test the implementation.
Furthermore, if you are looking for more examples to handle files (up- and download):
- #HOWTO: Up- and download files with React and Spring Boot
- #HOWTO: Up- and download files with Java EE and Web Components
Have fun handling files with RESTeasy on WildFly,
Phil