file downloading in restful web services - java

My requirement is, I should send a 10MB zip file to the client with a restful service. I found the code in forums that sending a StreamingOutput object is the better way, but how can I create a StreamingOutput object in the following 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 {
//------
} catch (Exception e) {
throw new WebApplicationException(e);
}
}
};
}

Its the better way and easy way for file dowload.
private static final String FILE_PATH = "d:\\Test2.zip";
#GET
#Path("/get")
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response getFile() {
File file = new File(FILE_PATH);
ResponseBuilder response = Response.ok((Object) file);
response.header("Content-Disposition", "attachment; filename=newfile.zip");
return response.build();
}
For your code as you asked:
#GET
#Path("/helloWorldZip")
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public StreamingOutput helloWorldZip() throws Exception {
return new StreamingOutput(){
#Override
public void write(OutputStream arg0) throws IOException, WebApplicationException {
// TODO Auto-generated method stub
BufferedOutputStream bus = new BufferedOutputStream(arg0);
try {
//ByteArrayInputStream reader = (ByteArrayInputStream) Thread.currentThread().getContextClassLoader().getResourceAsStream();
//byte[] input = new byte[2048];
java.net.URL uri = Thread.currentThread().getContextClassLoader().getResource("");
File file = new File("D:\\Test1.zip");
FileInputStream fizip = new FileInputStream(file);
byte[] buffer2 = IOUtils.toByteArray(fizip);
bus.write(buffer2);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
}

Related

Not able to read InputStream data in rest api

I am passing file/img(form data) from my angular app to my rest api as a post method body.
But i am not able to read inputStream content.
My Rest api method:-
#RequestMapping(path = "/upload", method = RequestMethod.POST)
#Consumes(MediaType.MULTIPART_FORM_DATA)
public void process(#FormDataParam("file") InputStream dataStream) throws IOException {
this.writeToFile(dataStream, "src/main/resources/targetFile.jpg");
}
private void writeToFile(InputStream uploadedInputStream, String uploadedFileLocation) {
try {
byte[] image = IOUtils.toByteArray(uploadedInputStream);
OutputStream out = new FileOutputStream(new File(uploadedFileLocation));
IOUtils.write(image, out);
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
image array is always empty and hence empty file gets created in destination dir.
Anything wrong with the implementation?
You can code rest service like this.
Controller method
#RequestMapping(value="/upload", method=RequestMethod.POST)
public void handleFileUpload(#RequestParam("file") MultipartFile file){
writeToFile(file, "src/main/resources/targetFile.jpg");
}
writeToFile method
private void writeToFile(MultipartFile file, String uploadedFileLocation) {
if (!file.isEmpty()) {
try {
byte[] bytes = file.getBytes();
BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(new File(uploadedFileLocation)));
stream.write(bytes);
stream.close();
} catch (Exception e) {
System.out.println(e.getMessage());
}
} else {
System.out.println("Empty file");
}
}

Spring REST for zip file download

I'm using the following REST method to be called from the UI to download the ZIP archive:
#RequstMapping("/download")
public void downloadFiles(HttpServletResponse response) {
response.setStatus(HttpServletResponse.SC_OK);
try {
downloadZip(response.getOutputStream());
} catch (IOException e) {
throw new RuntimeException("Unable to download file");
}
}
private void downloadZip(OutputStream output) {
try (ZipOutputStream zos = new ZipOutputStream(outputStream)) {
byte[] bytes = getBytes();
zos.write(bytes);
zos.closeEntry();
} catch (Exception e) {
throw new RuntimeException("Error on zip creation");
}
}
It's working fine, but I wanted to make the code more Spring oriented, e.g. to return ResponceEntity<Resource> instead of using ServletOutputStream of Servlet API.
The problem is that I couldn't find a way to create Spring resource from the ZipOutputStream.
Here is a way to return bytestream, you can use it to return zip file as well by setting content-type.
#RequestMapping(value = "/download", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity<Resource> download() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
InputStream is = null; // get your input stream here
Resource resource = new InputStreamResource(is);
return new ResponseEntity<>(resource, headers, HttpStatus.OK);
}

How to produce and consume rest service using jersey via application/octet_stream?

Firstly, I need to produce rest service for sending a POJO class include byte array field for image and other POJO class. Also need to consume this service using jersey client.It is possible to achive these using application/octet-stream MediaType. I already did it for only image file and it is working.
What is the correct way to do this?
public class Sample{
int sampleId;
Byte[] image;
Foo foo;
//constructor
//getter setter
}
public class GetDataImage {
#GET
#Path("/gets")
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response getFile(#QueryParam("id") String id ) throws IOException {
File file = new
File("..\test_image.jpg");
RenderedImage image2 = ImageIO.read(file);
Foo foo = new Foo();
Sample sample = new Sample (1, new Byte[] {},foo );
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectMapper mapper = new ObjectMapper(new BsonFactory());
mapper.writeValue(baos, responseChipoutImage);
StreamingOutput stream = new StreamingOutput() {
#Override
public void write(OutputStream output) throws IOException {
try {
// ImageIO.write(image2, "jpg", output);
new ObjectOutputStream(output).writeObject(responseChipoutImage);
} catch (Exception e) {
e.printStackTrace();
}
}
};
return Response.ok(stream, "application/octet-stream")
.header("content-disposition", "attachment; filename = " + image2.toString())
.build();
}
}
This is the client:
public class Client {
private static final String BASE_URI = "http://localhost:8090/Test/gets";
public Client () throws IOException {
try {
Client client = Client.create();
WebResource objWebResource = client.resource(BASE_URI);
ClientResponse response = objWebResource.path("/").queryParam("id", "1")
.type(javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM).get(ClientResponse.class);
System.out.println("response : " + response);
if (response.getStatus() == Status.OK.getStatusCode() && response.hasEntity()) {
ResponseSample responseSample = response.getEntity(ResponseSample.class);
// InputStream input = (InputStream)response.getEntity(InputStream.class);
// BufferedImage bf = ImageIO.read(input);
// File outputfile = new File("../test.jpeg");
// ImageIO.write(bf, "jpg", outputfile);
ObjectMapper mapper = new ObjectMapper(new BsonFactory());
// deserialize data
}
} catch (UniformInterfaceException e) {
e.printStackTrace();
} catch (ClientHandlerException e) {
e.printStackTrace();
}
}
public static void main(String... args) throws IOException {
new Client();
}
I found the solution finally. The solution is hidden in Jackson JSON parser --> Bson4jackson.
changed server side StreamingOutput ovveride method like this :
StreamingOutput stream = new StreamingOutput() {
#Override
public void write(OutputStream output) throws IOException {
try {
ObjectMapper mapper = new ObjectMapper(new BsonFactory());
mapper.writeValue(output, responseChipoutImage);
} catch (Exception e) {
e.printStackTrace();
}
}
};
and then catched the data from client adding jackson bson parser from InputStream.
public class Client {
private static final String BASE_URI = "http://localhost:8090/Test/gets";
public Client() throws IOException {
try {
Client client = Client.create();
WebResource objWebResource = client.resource(BASE_URI);
ClientResponse response = objWebResource.path("/").queryParam("id", "1")
.type(javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM).get(ClientResponse.class);
if (response.getStatus() == Status.OK.getStatusCode() && response.hasEntity()) {
ObjectMapper mapper = new ObjectMapper(new BsonFactory()).configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
ResponseSample responseSample = mapper.readValue(response.getEntityInputStream(), ResponseSample.class);
}
} catch (UniformInterfaceException e) {
e.printStackTrace();
} catch (ClientHandlerException e) {
e.printStackTrace();
}
}
public static void main(String...args) throws IOException {
new Client();
}

How to rewrite POST request body on HttpServletRequest

I'm working on a Filter in which I have to get the request payload, decrypt it, check if it's a valid JSON and if it is go on with the chain and go to my service. The thing is that, so far I haven't been able to find a way to rewrite the body. Why I want to rewrite it? As the service expects a JSON and the request has an encrypted text in the body, once I decrypt it I want the body to be the decrypted JSON. Also, once I return from the service, I should rewrite the response to have the json encrypted. I've read a lot of forums and questions but couldn't get to a working solution.
Here's my code:
RequestLoginFilter.java
#WebFilter("/RequestLoginFilter")
public class RequestLoginFilter implements Filter{
protected final static Log logger = LogFactory.getLog(RequestLoginFilter.class);
private ServletContext context;
private CryptoUtil crypto;
public void init(FilterConfig fConfig) throws ServletException {
this.context = fConfig.getServletContext();
this.context.log("RequestLoggingFilter initialized");
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// use wrapper to read multiple times the content
AuthenticationRequestWrapper req = new AuthenticationRequestWrapper((HttpServletRequest) request);
HttpServletResponse resp = (HttpServletResponse) response;
String payload = req.getPayload();
try {
String decryptedPayload = crypto.decrypt(payload);
JSONUtils.convertJSONStringToObject(decryptedPayload, LoginTokenTO.class);
} catch (GeneralSecurityException e) {
logger.error("Error when trying to decrypt payload '"+payload+"'");
throw new ServletException("Error when trying to decrypt payload '"+payload+"'", e);
}
chain.doFilter(req, resp);
System.out.println("a ver");
}
#Override
public void destroy() {
// TODO Auto-generated method stub
}
}
And also the wrapper, just in case:
AuthenticationRequestWrapper.java
public class AuthenticationRequestWrapper extends HttpServletRequestWrapper {
protected final static Log logger = LogFactory.getLog(AuthenticationRequestWrapper.class);
private final String payload;
public AuthenticationRequestWrapper (HttpServletRequest request) throws AuthenticationException {
super(request);
// read the original payload into the payload variable
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
try {
// read the payload into the StringBuilder
InputStream inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
// make an empty string since there is no payload
stringBuilder.append("");
}
} catch (IOException ex) {
logger.error("Error reading the request payload", ex);
throw new AuthenticationException("Error reading the request payload", ex);
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException iox) {
// ignore
}
}
}
payload = stringBuilder.toString();
}
#Override
public ServletInputStream getInputStream () throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(payload.getBytes());
ServletInputStream inputStream = new ServletInputStream() {
public int read ()
throws IOException {
return byteArrayInputStream.read();
}
};
return inputStream;
}
public String getPayload() {
return payload;
}
}
Hopefully somebody here knows how I can get to get this working.
Thanks in advance guys.
Whilst what you are asking is probably technically possible, it doesn't sound like the right approach to me.
What you need is a security layer that sits between the incoming request (endpoint) and your service. Re-writing the body of the request is a strange thing to be doing (which probably explains why you're having issues). Is there a reason you want this to be done in a Filter? After all, filters are designed to filter requests, not rewrite them ;)
A more logical/transparent solution would be to have your endpoint accept all incoming requests, decrypt and validate them before passing the request onto your service tier. Something like this:
public void handleRequest(Request request) {
try {
IncomingRequest x = securityManager.decrypt(request);
Response r = myService.handleRequest(x);
handleResponse(securityManager.encrypt(r));
}catch(InvlidateMessage x) {
handleInvalidMessage...
}catch(BusinessException x) {
handleBusinessException...
}
}

Constructing a DataSource from an InputStream or Byte array

I am writing a small file upload utility thing as part of a larger project. Originally I was handling this from a servlet using the Apache commons File utility classes. Here is a snippet from a quick test client I wrote for the service:
public static void main(String[] args) {
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.getInInterceptors().add(new LoggingInInterceptor());
factory.getOutInterceptors().add(new LoggingOutInterceptor());
factory.setServiceClass(FileUploadService.class);
factory.setAddress("http://localhost:8080/FileUploadService/FileUploadService");
FileUploadService client = (FileUploadService) factory.create();
FileType file = new FileType();
file.setName("statemo_1256144312279");
file.setType("xls");
DataSource source = new FileDataSource(new File("c:/development/statemo_1256144312279.xls"));
file.setHandler(new DataHandler(source));
Boolean ret = client.uploadFile(file);
System.out.println (ret);
System.exit(0);
}
This works absolutely fine. Now the problem comes when I am trying to replace the Apache commons utilities. In the above code I am creating a DataSource from a File with an absolute path name. In my servlet, I can't get an absolute path name however and the file I am sending over the wire is empty.
Here is the servlet code:
#SuppressWarnings("unchecked")
protected void doPost (final HttpServletRequest request, final HttpServletResponse response)
throws ServletException, IOException {
// form should have enctype="multipart/form-data" as an attribute
if (!ServletFileUpload.isMultipartContent (request)) {
LOG.info("Invalid form attribute");
return;
}
//DataInputStream in = new DataInputStream(request.getInputStream());
final DiskFileItemFactory factory = new DiskFileItemFactory ();
factory.setSizeThreshold(FILE_THRESHOLD_SIZE);
final ServletFileUpload sfu = new ServletFileUpload (factory);
sfu.setSizeMax(MAX_FILE_SIZE);
final HttpSession session = request.getSession();
final List<FileItem> files = new ArrayList<FileItem>();
final List<String> filesToProcess = new ArrayList<String>();
try {
final List<FileItem> items = sfu.parseRequest(request);
for (final FileItem f : items) {
if (!f.isFormField())
files.add(f);
}
/*for (final FileItem f : files) {
final String absoluteFileName = UPLOAD_DESTINATION + FilenameUtils.getName(f.getName());
//f.write(new File (absoluteFileName));
filesToProcess.add(absoluteFileName);
}*/
FileItem f = files.get(0);
LOG.info("File: " + FilenameUtils.getName(f.getName()));
LOG.info("FileBaseName: " + FilenameUtils.getBaseName(f.getName()));
LOG.info("FileExtension: " + FilenameUtils.getExtension(f.getName()));
FileUploadServiceClient client = new FileUploadServiceClient();
DataSource source = new FileDataSource(new File(f.getName()));
FileType file = new FileType();
file.setHandler(new DataHandler(source));
file.setName(FilenameUtils.getBaseName(f.getName()));
file.setType(FilenameUtils.getExtension(f.getName()));
Boolean ret = client.uploadFile(file);
LOG.info("File uploaded - " + ret);
filesToProcess.add(UPLOAD_DESTINATION + FilenameUtils.getName(f.getName()));
session.setAttribute("filesToProcess", filesToProcess);
final RequestDispatcher dispatcher = request.getRequestDispatcher("Validate");
if (null != dispatcher) {
dispatcher.forward(request, response);
}
} catch (FileUploadException e) {
LOG.info("Exception " + e.getMessage());
e.printStackTrace();
} catch (Exception e) {
LOG.info("Exception " + e.getMessage());
e.printStackTrace();
}
}
I've been working on this for the better part of this morning and am not getting anywhere. Even if I get rid of the Apache commons file stuff completely and handle the parsing of the request myself, I still can't construct the DataSource appropriately.
Thanks!
This was rather simple actually, I just copied over the bytes from the InputStream to the DataSource:
FileItem f = files.get(0);
// there is a problem here where the file being created is empty, since we only have a
// partial path:
DataSource source = new FileDataSource(new File(f.getName()));
// because of the above problem, we are going to copy over the data ourselves:
byte[] sourceBytes = f.get();
OutputStream sourceOS = source.getOutputStream();
sourceOS.write(sourceBytes);
This is the code of commons-email ByteArrayDataSource
it sounds odd to try to replace apache commons - don't, unless you have a really good reason
you can get absolute paths in a servlet. You can call getServletContext().getRealPath("/") which will return the absolute path of your application, and then you can get files relative to it.
In our application there are objects that have properties InputStream and Name. We are using next class to construct DataSource with those properties.
public class InputStreamDataSource implements DataSource {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
private final String name;
public InputStreamDataSource(InputStream inputStream, String name) {
this.name = name;
try {
int nRead;
byte[] data = new byte[16384];
while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
inputStream.close();
buffer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public String getContentType() {
return new MimetypesFileTypeMap().getContentType(name);
}
#Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(buffer.toByteArray());
}
#Override
public String getName() {
return name;
}
#Override
public OutputStream getOutputStream() throws IOException {
throw new IOException("Read-only data");
}
}
Most of the solutions shown here require that the InpustStream be closed (read into memory). It is possible to wrap the InputStream in a DataSource object without closing the InputStream though:
private record PipedDataSource(InputStream in, String contentType, String encoding)
implements DataSource, EncodingAware {
public String getContentType() {
return contentType;
}
public InputStream getInputStream() {
return in;
}
public String getName() {
return "PipedDataSource";
}
public OutputStream getOutputStream() throws IOException {
throw new IOException("No OutputStream");
}
#Override
public String getEncoding() {
return encoding;
}
}
The example above also implements EncodingAware. This can prevent the InputStream from being closed by third part libraries (for example java.mail.internet.MimeUtility) when they get the data source encoding.

Categories

Resources