Download a file through Spring MVC controller using streams - java

I am using spring MVC with REST service for one of my project. I am having a service to attach and download user files.
I am using below service API for upload and save file into server directory
http://myrestserver/attachmentService/attach/userKey
And below service API for download files from server directory
http://myrestserver/attachmentService/download/userKey/fileKey
The issue is that when a file is downloaded, the downloaded URL shows the REST service API URL. To avoid this, I thought of write a controller for attach and download file.
I wrote a spring controller which handle file attachment process. Even I wrote a controller(say download.do) for download a file, but when a file downloaded, the file name shows as the same name of the controller(downloaded file name shows "download.do" always) instead of original file name.
Below code is from my download.do controller
WebResource resource = null;
resource = client.resource("http://myrestserver/attachmentService/download/userKey/fileKey");
clientResponse = resource.accept(MediaType.APPLICATION_OCTET_STREAM).get(
ClientResponse.class);
InputStream inputStream = clientResponse.getEntityInputStream();
if(inputStream != null){
byteArrayOutputStream = new ByteArrayOutputStream();
try {
IOUtil.copyStream(inputStream, byteArrayOutputStream);
} catch (IOException e) {
log.error("Exception in download:"+ e);
}
}
And, in my service API, the code is
file = new File(directory, attachmentFileName);
fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(attachmentContent);
fileOutputStream.close();
response = Response.ok((Object) file).type(MediaType.APPLICATION_OCTET_STREAM);
response.header("Content-Disposition", "attachment; filename=" + "\"" + attachmentFileName
+ "\"");
return response.build();
By analyzing the issue, I understood that, am not setting file header in downloaded file through download.do controller.
If I am using outstream in download.do controller, I will not be able to set the file header.
Can any one help me to resolve this issue. My primary aim is to hide my rest service URL from downloaded file by stream through a MVC controller.
I found a post (Downloading a file from spring controllers )in stack overflow almost like my question, but the file type is previously known. Please note that, in my application user can attach any type of file.

You have to set the Content-Disposition prior to writing the file to the output stream. Once you start writing to the output stream, you cannot set headers any longer.

Related

Java - Sending file over HTTP Server

I made a HTML server using com.sun.net.httpserver library. I want to send a jar file to the client to make them download it.
This method below actually make the client download the file:
#Override
public void handle(HttpExchange httpExchange) {
File file = new File("Test.jar");
try {
httpExchange.sendResponseHeaders(200, file.length());
OutputStream outputStream = httpExchange.getResponseBody();
Files.copy(file.toPath(), outputStream);
outputStream.close();
} catch (IOException exception) {
exception.printStackTrace();
}
}
but it sends the jar file as a zip. How do I get it to send it as a jar file instead? And is there a better way to send files?
Please try adding the following to get correct filename for the download:
httpExchange.getResponseHeaders().add("Content-Disposition", "attachment; filename=Test.jar");
You might also want do add the following to get the corrent content-type:
httpExchange.setAttribute(HTTPExchange.HeaderFields.Content_Type.toString(), "application/java-archive");
Please see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types for a listing of content-types for different suffixes.

Unable to download file from swagger

#ApiResponses(value = {
#ApiResponse(code = 200, message = "Perfect") })
public void getLabel(#QueryParam("orderItemsId") String orderItemsId,HttpServletRequest httpServletRequest,HttpServletResponse response) {
String dataDirectory = httpServletRequest.getSession().getServletContext().getRealPath("/WEB-INF/files/label.pdf");
Path file = Paths.get(dataDirectory);
if (Files.exists(file))
{
response.setContentType("application/pdf");
response.addHeader("Content-Disposition", "attachment; filename=\"label.pdf\"");
try
{
Files.copy(file, response.getOutputStream());
response.getOutputStream().flush();
}
catch (IOException ex) {
ex.printStackTrace();
}
}
}
I am using springfox-swagger2 - version 2.5.0 with springfox-swagger-ui - version 2.5.0.
without content-Disposition header swagger is unable to sync output file in swagger-ui, it shows data in binary form(i guess)kind-of corrupted and whereas with this header i get a link in ResponseBody which also downloads pdf but corrupted form same as it syncs in swagger-ui.
I have done a research it shows we need to provide datatype:"file" in response link . but #ApiResponse doesn't contain any datatype field. Though it has field with response but i am not sure what class to give for
octet-stream output. I have tried OutputStream but it doesn't work.
Edit : Swagger UI does not support the downloading of file. whereas same url if called through other source will do the job.
Swagger UI does not support the downloading of file. whereas same url if called through other source will do the job.

is it possible to cache pdf file in web page?

I have a jsp page that is used to download pdf file from server. When I request the jsp page from browser, the jsp read file info from database, get inputstream from file and write to ServletOutputStream.
The pseudo-code like this:
response.setContentType("application/pdf");
response.setHeader("Content-disposition","inline;filename=URLEncoder.encode(filename)");
response.setHeader("Cache-Control","max-age=3600");
ServletOutputStream os = response.getOutputStream();
FileInputStream in = new FileInputStream(new File(filePath));
int size = 0;
byte[] buffer = new byte[512];
while((size=in.read(buffer))!=-1){
os.write(buffer,0,size);
}
when the request finished, the downloaded pdf was opened in browser. The same file is downloaded many times, so we want to cache them to the local file system, so that each request's return status code is 304(not modified), but the cache-control doesn't work, is there anybody can help?
This problem is typically solved by a reverse proxy like NGINX or Varnish in front of your application. Alternatively you can use a CDN.

How can i read a file in a ftp server using a servlet and then send this as a downloadable file to the user?

I have developed a servlet that offers some services.
I am using apache-commons-net FTPClient to log into a ftp server and read a file.
I want to make this file downloadle (aka send it to the outputstream maybe?) , but the only ways of reading a file that i know of are:
FTPClient.retrieveFileStream(String remote) and FTPClient.retrieveFile(String remote, OutputStream local).
I tried the first one and then wrote the InputStream i got to the outputStream of the servlet:
InputStream myFileStream = FTPClient.retrieveFileStream(fileName);
byte[] buffer = new byte[4096];
int length;
resp.reset();
resp.setContentType("text/csv");
resp.setHeader("Content-disposition","attachment; filename=\""+fileName+"\"");
OutputStream out = resp.getOutputStream();
while((length=myFileStream.read(buffer)) > 0){
out.write(buffer, 0, length);
}
myFileStream.close();
out.flush();
The Second One:
myClient.retrieveFile(fileName, resp.getOutputStream());
In both cases i get the text content of the file as a response and not the file itself.
Is there any way i can do this.
P.s. this code belongs to a medhod that is being called by the doPost() with http req and http resp as parameters.
If you want to download the file instead of just showing it, you have to change the content type you're sending to the browser (because it's browser's business to either display the data or save them as a file). Thus, do e.g.
resp.setContentType("application/octet-stream");
(instead of text/csv) to "hide" the real nature of the data from the browser and force it to save the data.
The problem was that i was using a google extension (DHC) to test my web service. and it displayed the file content instead of initializing the download.
I was making the file download in a doPost() method.
Solution:
I made it in a doGet() method and when accessed directly via browser everything works ok.
So i think it was only the extensions problem, which wrote the content of the response back to me instead of downloading the file attachment.
Thanks for the feedback to #Jozef

Jersey servlet returns zip file that contains more bytes than response sent

I'm trying to implement a simple servlet that returns a zip file that is bundled inside the application (simple resource)
So I've implemented the following method in the server side:
#GET
#Path("{path}/{zipfile}")
#Produces("application/zip")
public Response getZipFile(
#PathParam("path") String pathFolder,
#PathParam("zipfile") String zipFile) IOException {
String fullPath= String.format("/WEB-INF/repository/%s/%s",
pathFolder, zipFile);
String realPath = ServletContextHolder.INSTANCE.getServletContext()
.getRealPath(fullPath);
File file = new File(realPath );
ResponseBuilder response = Response.ok((Object) file);
return response.build();
}
When I call this method from the borwser, the zip file is downloaded and its size is the same number of bytes as the original zip in the server.
However, when I call this using a simple XMLHttpRequest from my client side code:
var oXHR = new XMLHttpRequest();
var sUrl = "http://localhost:8080/path/file.zip"
oXHR.open('GET', sUrl);
oXHR.responseType = 'application/zip';
oXHR.send();
I can see in the Network tab of the Developer tools in chrome that the content size is bigger, and I'm unable to process this zip file (for instance JSzip doesn't recognize it).
It seems like somewhere between my response and the final response from org.glassfish.jersey.servlet.ServletContainer, some extra bytes are written/ some encoding is done on the file.
Can you please assist?
Best Regards,
Maxim
When you use an ajax request, the browser expects text (by default) and will try to decode it from UTF-8 (corrupting your data).
Try with oXHR.responseType = "arraybuffer"; : that way, the browser won't change the data and give you the raw content (which will be in oXHR.response).
This solution won't work in IE 6-9 : if you need to support it, check JSZip documentation : http://stuk.github.io/jszip/documentation/howto/read_zip.html
If it's not the right solution, try downloading directly the zip file (without any js code involved) to check if the issue comes from the js side or from the java side.

Categories