Input and Output binary streams using JERSEY?

JavaJerseyJax Rs

Java Problem Overview


I'm using Jersey to implement a RESTful API that is primarily retrieve and serve JSON encoded data. But I have some situations where I need to accomplish the following:

  • Export downloadable documents, such as PDF, XLS, ZIP, or other binary files.
  • Retrieve multipart data, such some JSON plus an uploaded XLS file

I have a single-page JQuery-based web client that creates AJAX calls to this web service. At the moment, it doesn't do form submits, and uses GET and POST (with a JSON object). Should I utilize a form post to send data and an attached binary file, or can I create a multipart request with JSON plus binary file?

My application's service layer currently creates a ByteArrayOutputStream when it generates a PDF file. What is the best way to output this stream to the client via Jersey? I've created a MessageBodyWriter, but I don't know how to use it from a Jersey resource. Is that the right approach?

I've been looking through the samples included with Jersey, but haven't found anything yet that illustrates how to do either of these things. If it matters, I'm using Jersey with Jackson to do Object->JSON without the XML step and am not really utilizing JAX-RS.

Java Solutions


Solution 1 - Java

I managed to get a ZIP file or a PDF file by extending the StreamingOutput object. Here is some sample code:

@Path("PDF-file.pdf/")
@GET
@Produces({"application/pdf"})
public StreamingOutput getPDF() throws Exception {
    return new StreamingOutput() {
        public void write(OutputStream output) throws IOException, WebApplicationException {
            try {
                PDFGenerator generator = new PDFGenerator(getEntity());
                generator.generatePDF(output);
            } catch (Exception e) {
                throw new WebApplicationException(e);
            }
        }
    };
}

The PDFGenerator class (my own class for creating the PDF) takes the output stream from the write method and writes to that instead of a newly created output stream.

Don't know if it's the best way to do it, but it works.

Solution 2 - Java

I had to return a rtf file and this worked for me.

// create a byte array of the file in correct format
byte[] docStream = createDoc(fragments); 

return Response
            .ok(docStream, MediaType.APPLICATION_OCTET_STREAM)
            .header("content-disposition","attachment; filename = doc.rtf")
            .build();



	

Solution 3 - Java

I'm using this code to export excel (xlsx) file ( Apache Poi ) in jersey as an attachement.

@GET
@Path("/{id}/contributions/excel")
@Produces("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
public Response exportExcel(@PathParam("id") Long id)  throws Exception  {

	Resource resource = new ClassPathResource("/xls/template.xlsx");
	
	final InputStream inp = resource.getInputStream();
	final Workbook wb = WorkbookFactory.create(inp);
	Sheet sheet = wb.getSheetAt(0);
	
	Row row = CellUtil.getRow(7, sheet);
	Cell cell = CellUtil.getCell(row, 0);
	cell.setCellValue("TITRE TEST");

    [...]
	
	StreamingOutput stream = new StreamingOutput() {
        public void write(OutputStream output) throws IOException, WebApplicationException {
            try {
            	wb.write(output);
            } catch (Exception e) {
                throw new WebApplicationException(e);
            }
        }
    };


	return Response.ok(stream).header("content-disposition","attachment; filename = export.xlsx").build();

}

Solution 4 - Java

Here's another example. I'm creating a QRCode as a PNG via a ByteArrayOutputStream. The resource returns a Response object, and the stream's data is the entity.

To illustrate the response code handling, I've added handling of cache headers (If-modified-since, If-none-matches, etc).

@Path("{externalId}.png")
@GET
@Produces({"image/png"})
public Response getAsImage(@PathParam("externalId") String externalId, 
		@Context Request request) throws WebApplicationException {

	ByteArrayOutputStream stream = new ByteArrayOutputStream();
	// do something with externalId, maybe retrieve an object from the
	// db, then calculate data, size, expirationTimestamp, etc

	try {
		// create a QRCode as PNG from data		
		BitMatrix bitMatrix = new QRCodeWriter().encode(
				data, 
				BarcodeFormat.QR_CODE, 
				size, 
				size
		);
		MatrixToImageWriter.writeToStream(bitMatrix, "png", stream);

	} catch (Exception e) {
		// ExceptionMapper will return HTTP 500 
		throw new WebApplicationException("Something went wrong …")
	}

	CacheControl cc = new CacheControl();
	cc.setNoTransform(true);
	cc.setMustRevalidate(false);
	cc.setNoCache(false);
	cc.setMaxAge(3600);
			
	EntityTag etag = new EntityTag(HelperBean.md5(data));

	Response.ResponseBuilder responseBuilder = request.evaluatePreconditions(
			updateTimestamp,
			etag
	);
	if (responseBuilder != null) {
		// Preconditions are not met, returning HTTP 304 'not-modified'
		return responseBuilder
				.cacheControl(cc)
				.build();
	}

	Response response = Response
			.ok()
			.cacheControl(cc)
			.tag(etag)
			.lastModified(updateTimestamp)
			.expires(expirationTimestamp)
			.type("image/png")
			.entity(stream.toByteArray())
			.build();
	return response;
}	

Please don't beat me up in case stream.toByteArray() is a no-no memory wise :) It works for my <1KB PNG files...

Solution 5 - Java

I have been composing my Jersey 1.17 services the following way:

FileStreamingOutput

public class FileStreamingOutput implements StreamingOutput {

	private File file;

	public FileStreamingOutput(File file) {
		this.file = file;
	}

	@Override
	public void write(OutputStream output)
			throws IOException, WebApplicationException {
		FileInputStream input = new FileInputStream(file);
		try {
			int bytes;
			while ((bytes = input.read()) != -1) {
				output.write(bytes);
			}
		} catch (Exception e) {
			throw new WebApplicationException(e);
		} finally {
			if (output != null) output.close();
			if (input != null) input.close();
		}
	}

}

GET

@GET
@Produces("application/pdf")
public StreamingOutput getPdf(@QueryParam(value="name") String pdfFileName) {
	if (pdfFileName == null)
		throw new WebApplicationException(Response.Status.BAD_REQUEST);
	if (!pdfFileName.endsWith(".pdf")) pdfFileName = pdfFileName + ".pdf";

	File pdf = new File(Settings.basePath, pdfFileName);
	if (!pdf.exists())
		throw new WebApplicationException(Response.Status.NOT_FOUND);

	return new FileStreamingOutput(pdf);
}

And the client, if you need it:

Client

private WebResource resource;

public InputStream getPDFStream(String filename) throws IOException {
	ClientResponse response = resource.path("pdf").queryParam("name", filename)
		.type("application/pdf").get(ClientResponse.class);
	return response.getEntityInputStream();
}

Solution 6 - Java

This example shows how to publish log files in JBoss through a rest resource. Note the get method uses the StreamingOutput interface to stream the content of the log file.

@Path("/logs/")
@RequestScoped
public class LogResource {

private static final Logger logger = Logger.getLogger(LogResource.class.getName());
@Context
private UriInfo uriInfo;
private static final String LOG_PATH = "jboss.server.log.dir";

public void pipe(InputStream is, OutputStream os) throws IOException {
    int n;
    byte[] buffer = new byte[1024];
    while ((n = is.read(buffer)) > -1) {
        os.write(buffer, 0, n);   // Don't allow any extra bytes to creep in, final write
    }
    os.close();
}

@GET
@Path("{logFile}")
@Produces("text/plain")
public Response getLogFile(@PathParam("logFile") String logFile) throws URISyntaxException {
    String logDirPath = System.getProperty(LOG_PATH);
    try {
        File f = new File(logDirPath + "/" + logFile);
        final FileInputStream fStream = new FileInputStream(f);
        StreamingOutput stream = new StreamingOutput() {
            @Override
            public void write(OutputStream output) throws IOException, WebApplicationException {
                try {
                    pipe(fStream, output);
                } catch (Exception e) {
                    throw new WebApplicationException(e);
                }
            }
        };
        return Response.ok(stream).build();
    } catch (Exception e) {
        return Response.status(Response.Status.CONFLICT).build();
    }
}

@POST
@Path("{logFile}")
public Response flushLogFile(@PathParam("logFile") String logFile) throws URISyntaxException {
    String logDirPath = System.getProperty(LOG_PATH);
    try {
        File file = new File(logDirPath + "/" + logFile);
        PrintWriter writer = new PrintWriter(file);
        writer.print("");
        writer.close();
        return Response.ok().build();
    } catch (Exception e) {
        return Response.status(Response.Status.CONFLICT).build();
    }
}    

}

Solution 7 - Java

Using Jersey 2.16 File download is very easy.

Below is the example for the ZIP file

@GET
@Path("zipFile")
@Produces("application/zip")
public Response getFile() {
    File f = new File(ZIP_FILE_PATH);

    if (!f.exists()) {
        throw new WebApplicationException(404);
    }

    return Response.ok(f)
            .header("Content-Disposition",
                    "attachment; filename=server.zip").build();
}

Solution 8 - Java

I found the following helpful to me and I wanted to share in case it helps you or someone else. I wanted something like MediaType.PDF_TYPE, which doesn't exist, but this code does the same thing:

DefaultMediaTypePredictor.CommonMediaTypes.
        getMediaTypeFromFileName("anything.pdf")

See <http://jersey.java.net/nonav/apidocs/1.1.0-ea/contribs/jersey-multipart/com/sun/jersey/multipart/file/DefaultMediaTypePredictor.CommonMediaTypes.html>

In my case I was posting a PDF document to another site:

FormDataMultiPart p = new FormDataMultiPart();
p.bodyPart(new FormDataBodyPart(FormDataContentDisposition
        .name("fieldKey").fileName("document.pdf").build(),
        new File("path/to/document.pdf"),
        DefaultMediaTypePredictor.CommonMediaTypes
                .getMediaTypeFromFileName("document.pdf")));

Then p gets passed as the second parameter to post().

This link was helpful to me in putting this code snippet together: <http://jersey.576304.n2.nabble.com/Multipart-Post-td4252846.html>

Solution 9 - Java

This worked fine with me url:http://example.com/rest/muqsith/get-file?filePath=C:\Users\I066807\Desktop\test.xml

@GET
@Produces({ MediaType.APPLICATION_OCTET_STREAM })
@Path("/get-file")
public Response getFile(@Context HttpServletRequest request){
   String filePath = request.getParameter("filePath");
   if(filePath != null && !"".equals(filePath)){
    	File file = new File(filePath);
    	StreamingOutput stream = null;
    	try {
		final InputStream in = new FileInputStream(file);
		stream = new StreamingOutput() {
			public void write(OutputStream out) throws IOException, WebApplicationException {
				try {
					int read = 0;
			      		byte[] bytes = new byte[1024];
			         
			        	while ((read = in.read(bytes)) != -1) {
			        		out.write(bytes, 0, read);
			       		}
				} catch (Exception e) {
					throw new WebApplicationException(e);
				}
			}
		};
	} catch (FileNotFoundException e) {
		e.printStackTrace();
	}
    	return Response.ok(stream).header("content-disposition","attachment; filename = "+file.getName()).build();
    	}
    return Response.ok("file path null").build();
}

Solution 10 - Java

Another sample code where you can upload a file to the REST service, the REST service zips the file, and the client downloads the zip file from the server. This is a good example of using binary input and output streams using Jersey.

https://stackoverflow.com/a/32253028/15789

This answer was posted by me in another thread. Hope this helps.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTaurenView Question on Stackoverflow
Solution 1 - JavaMikeTheReaderView Answer on Stackoverflow
Solution 2 - JavaAbhishek RakshitView Answer on Stackoverflow
Solution 3 - JavaGrégoryView Answer on Stackoverflow
Solution 4 - JavaHankView Answer on Stackoverflow
Solution 5 - JavaDaniel SzalayView Answer on Stackoverflow
Solution 6 - JavaJaime CaseroView Answer on Stackoverflow
Solution 7 - JavaorangegiraffaView Answer on Stackoverflow
Solution 8 - JavaDovev HefetzView Answer on Stackoverflow
Solution 9 - JavaMuqsithView Answer on Stackoverflow
Solution 10 - JavaRuntimeExceptionView Answer on Stackoverflow