I am trying to download a zip file from a fixed location present in server.
In my Rest method , I am just passing the file name from client (browser) .
(Please see below code ).
In my Rest method I am sending the zip file to the client.
The file gets downloaded on the browser without any issue.
My Issue is that the zip file gets downloaded on browser without .zip extension.
#RequestMapping(value = "/zip/{filePath}", method = RequestMethod.GET)
public #ResponseBody void downloadZip(#PathVariable("filePath") String filePath, HttpServletRequest request, HttpServletResponse response) throws IOException {
ServletContext context = request.getServletContext();
File downloadFile = new File(filePath);
FileInputStream inputStream = new FileInputStream(downloadFile);
// get output stream of the response
OutputStream outStream = response.getOutputStream();
byte[] buffer = new byte[(int) downloadFile.length()];
int bytesRead = -1;
// write bytes read from the input stream into the output stream
while ((bytesRead = inputStream.read(buffer)) != -1) {
outStream.write(buffer, 0, bytesRead);
}
// get MIME type of the file
String mimeType = context.getMimeType(fullPath);
if (mimeType == null) {
// set to binary type if MIME mapping not found
mimeType = "application/octet-stream";
}
System.out.println("MIME type: " + mimeType);
// set content attributes for the response
response.setContentType(mimeType);
response.setContentLength((int) downloadFile.length());
response.setHeader("Content-Disposition",
String.format("attachment; filename=\"%s\"", downloadFile.getName()));
logger.error("Filename = " + downloadFile.getName());
inputStream.close();
outStream.close();
}
PS: The file gets downloaded on some machine with ZIP and in some machine without ZIP. I have tested only on chrome (as per client requirement).
I think, there is an issue with the Chrome settings which I need to look upon (just a guess).
Can someone help upon this?
Thanks in advance....
Change the order between setting the response headers and shoving the file down the output stream - after all, the headers need to leave first.
[Edited]
"Why setting HttpServletResponse in starting effects the code."
Well, simple: the client is supposed to receive instructions of what to do with the payload by interpreting the HTTP response headers. If those are not set in the beginning, sending those headers at the end of the transmission comes too late. And this assumes the HttpServletResponse will actually send those headers when invoked with setHeader, which is a big assumption - I suspect those headers will not actually be sent after calling response.getOutputStream - it is unlikely the response will buffer the entire payload to wait for the caller to specify those headers.
Related
I have a HTTP POST method that works fine if I upload text files. But if I try to upload a word document, pdf, zip, gzip, etc... the files that are uploaded get corrupted in the process. I'm using Postman to send the request. I do a "POST" method, enter the url, add headers (tried all sorts of headers and it really does not change anything so now I don't have any entered), and then on the body I select "formdata" and select the file. I really just need to fix this to be able to support files that end in .csv.gz and .csv. Currently, csv is fine but the .csv.gz is the type that is corrupting. I tried other non-text files as well just to see what happens and they corrupt too. I cannot figure out if there is some encoding, filter, etc... that is causing this to happen that I can remove or some setting I need to apply. Or if there is some other way to handle this with jersey so the non-text files stay the same as the original file.
My application is running Spring v1.5.3 and Jersey 2.25.
#Override
public Response uploadTopicFile(String topic, FormDataMultiPart formDataMultipart) throws Exception {
List<BodyPart> bodyParts = formDataMultipart.getBodyParts();
// Getting the body of the request (should be a file)
for (BodyPart bodyPart : bodyParts) {
String fileName = bodyPart.getContentDisposition().getFileName();
InputStream fileInputStream = bodyPart.getEntityAs(InputStream.class);
String uploadedFileLocation = env.getProperty("temp.upload.path") + File.separator + fileName;
this.saveFile(fileInputStream, uploadedFileLocation);
String output = "File uploaded to : " + uploadedFileLocation;
log.debug(output);
}
return Response.status(201).build();
}
private void saveFile(InputStream uploadedInputStream, String serverLocation) {
try {
// Create the output directory
Files.createDirectories(Paths.get(serverLocation).getParent());
// Get the output stream
OutputStream outputStream = new FileOutputStream(new File(serverLocation));
int read = 0;
byte[] bytes = new byte[1024];
// Loop through the stream
while ((read = uploadedInputStream.read(bytes)) != -1) {
// Output to file
outputStream.write(bytes, 0, read);
}
// Flush and close
outputStream.flush();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
return;
}
There was a filter causing the corruption. Filter was updated and issue resolved.
I have PDFs mounted on an external server. I have to access them in my Java servlet and push them to the clients browser. The PDF should get downloaded directly or it may open a 'SAVE or OPEN' dialog window.
This is what i am trying in my code but it could not do much.
URL url = new URL("http://www01/manuals/zseries.pdf");
ByteArrayOutputStream bais = new ByteArrayOutputStream();
InputStream in = url.openStream();
int FILE_CHUNK_SIZE = 1024 * 4;
byte[] chunk = new byte[FILE_CHUNK_SIZE];
int n =0;
while ( (n = in.read(chunk)) != -1 ) {
bais.write(chunk, 0, n);
}
I have tried many ways to do this but could not succeed. I welcome if you have any good method to do this!
When you read the data, you get it inside your program memory, which is on the server side. To get it to the user's browser, you have to also write everything that you have read.
Before you start writing, though, you should give some appropriate headers.
Indicate that you are sending over a PDF file, by setting the mime type
Set the content length.
Indicate that the file is intended for download rather than showing inside the browser.
To set the mime type, use
response.setContentType("application/pdf");
To set the content length, assuming it's the same content length that you get from the URL, use:
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.connect();
if ( connection.getResponseCode() == 200 ) {
int contentLength = connection.getContentLength();
response.setContentLength( contentLength );
To indicate that you want the file to be downloaded, use:
response.setHeader( "Content-Disposition", "attachment; filename=\"zseries.pdf\"";
(Take care to change the file name to whatever you want the user to see in the save dialog box)
Finally, get the input stream from the URLConnection you just opened, get the servlet's response output stream, and start reading from one and writing to the other:
InputStream pdfSource = connection.getInputStream();
OutputStream pdfTarget = response.getOutputStream();
int FILE_CHUNK_SIZE = 1024 * 4;
byte[] chunk = new byte[FILE_CHUNK_SIZE];
int n =0;
while ( (n = pdfSource.read(chunk)) != -1 ) {
pdfTarget.write(chunk, 0, n);
}
} // End of if
Remember to use try/catch around this, because most of these methods throw IOException, timeout exceptions etc., and to finally close both streams. Also remember to do something meaningful (like give an error output) in case the response was not 200.
You could transfer the byte array to the client, then use Itext to "stamp" the pdf in a new file. After that use java.awt.Desktop to lauch the file.
public static void lauchPdf(byte[] bytes, String fileName) throws DocumentException, IOException{
PdfReader reader = new PdfReader(bytes);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(fileName));
stamper.close();
Desktop dt = Desktop.getDesktop();
dt.browse(getFileURI(fileName));
}
You don't need to push anything (hope you really don't, because actually you can't). From the perspective of the browser making the request, you could get the PDF from the database, generate it on the fly or read it from the filesystem (which is your case). So, let's say you have this in your HTML:
DOWNLOAD FILE
you need to register a servlet for /dl/* and implement the doGet(req, resp) like this:
public void doGet(
HttpServletRequest req
, HttpServletResponse resp
) throws IOException {
resp.setContentType("application/pdf");
response.setHeader("Content-Disposition",
"attachment; filename=\"" + suggestFilename(req) + "\"");
// Then copy the stream, for example using IOUtils.copy ...
// lookup the URL from the bits after /dl/*
URL url = getURLFromRequest(req);
InputStream in = url.openConnection().getInputStream();
IOUtils.copy(in, resp.getOutputStream());
fin.close();
}
IOUtils is from Apache Commons IO (or just write your own while loop)
I have an ipcamera that whenever multiple of users are connecting to it it becomes too slow.
I was thinking about getting the stream from the camera with my server and multiple of clients should be able to stream from the server instead of the poor ipcamera.
i set up a quick and dirty servlet just too see if it works :
#RequestMapping(value = "/", method = RequestMethod.GET, produces = "application/x-shockwave-flash")
public String getVideoStream(Locale locale, Model model, HttpServletRequest request, HttpServletResponse response) throws IOException {
logger.info("Start");
// An IPcamera stream example
URL url = new URL("http://www.earthcam.com/swf/ads5.swf");
URLConnection yc = url.openConnection();
OutputStream out = response.getOutputStream();
InputStream in = yc.getInputStream();
String mimeType = "application/x-shockwave-flash";
byte[] bytes = new byte[100000];
int bytesRead;
response.setContentType(mimeType);
while ((bytesRead = in.read(bytes)) != -1) {
out.write(bytes, 0, bytesRead);
}
logger.info("End");
I believe this might work, my problem right now is that :
bytesRead = in.read(bytes)
reads only 61894 bytes and that's it :( why is that happening? am i trying to get the stream wrong?
btw: i tried to do this with xuggler, but i had an error that compressed-SWF not supported.
thanks
Your code is working perfectly. I just fetched ads5.swf from your server and it is, indeed, 61894 bytes in length. The problem you're facing is that the SWF file is just the movie player. After being downloaded, the player then fetches the video stream from the server. By default (if this is some kind of turn-key streaming solution), it's probably trying to get the stream from the same server where the SWF comes from.
I have a Java Servlet that generates randomly thousands of Strings every time is called. I want the user to be able to get them in a file when he calls the Servlet. I don't want to write first the file on disk or memory.
Is there a way to write the file on the fly when the user calls the servlet?
Thanks
Any text that you generate in the Servlet can simply be written to the OutputStream returned by ServletResponse.getOutputStream().
If you want the output to be downloadable as a file, you can follow the approach in this answer - https://stackoverflow.com/a/11772700/1372207
The difference would be, that the Content-type would be text/plain and instead of reading from another inputstream, you would just write the String objects directly to the ServletOutputStream using the print(String) method.
If you use the idea to write content to HttpServletResponse's output stream while offering download service, rather than saving the content locally and then reading the file as FileInputStream, you can just convert the file content to InputStream by InputStream stream = new ByteArrayInputStream(exampleString.getBytes("UTF-8"));.
The following code partially references https://www.codejava.net/java-ee/servlet/java-servlet-download-file-example.
public void doDownload(HttpServletRequest request, HttpServletResponse response) throws IOException {
String fileName = "xxx.txt";
String fileContent = "";
// get absolute path of the application
ServletContext context = request.getServletContext();
// get MIME type of the file
String mimeType = context.getMimeType(fileName);
if (mimeType == null) {
// set to binary type if MIME mapping not found
mimeType = "application/octet-stream";
}
setResponseHeader(response, fileName, mimeType, (int) fileContent.length());
InputStream inputStream = new ByteArrayInputStream(fileContent.getBytes("UTF-8"));
// get output stream of the response
OutputStream outStream = response.getOutputStream();
byte[] buffer = new byte[4096];
int bytesRead = -1;
// write bytes read from the input stream into the output stream
while ((bytesRead = inputStream.read(buffer)) != -1) {
outStream.write(buffer, 0, bytesRead);
}
inputStream.close();
outStream.close();
}
private void setResponseHeader(HttpServletResponse response, String fileName, String mimeType, Integer fileLength) {
response.setContentType(mimeType);
response.setContentLength(fileLength);
response.setContentType("application/octet-stream; charset=UTF-8");
String headerKey = "Content-Disposition";
String headerValue = String.format("attachment; filename=\"%s\"", fileName);
response.setHeader(headerKey, headerValue);
response.addHeader("Pargam", "no-cache");
response.addHeader("Cache-Control", "no-cache");
}
I have a Servlet which is returning a csv file that is 'working' over HTTP in both internet explorer and firefox. When I execute the same Servlet over HTTPS only firefox continues to download the csv file over HTTPS. I don't think this is necessarily an Internet 6 or 7 issue described on MSDN :
The message is:
Internet Explorer cannot download
data.csv from mydomain.com Internet
Explorer was not able to open this
Internet site. The requested site is
either unavailable or cannot be found.
Please try again later.
Please note that the site is still 'up' after this message and you can continue to browse the site, its just the download of the CSV that prompts this message. I have been able to access similar files over https on IE from other j2ee applications so I believe it is our code. Should we not be closing the bufferedOutputStream?
UPDATE
whether to close or not to close the output stream:
I asked this question on the java posse forums and the discussion there is also insightful. In the end it seems that no container should rely on the 'client' (your servlet code in this case) to close this output stream. So if your failure to close the stream in your servlet causes a problem it is more a reflection on the poor implementation of your servlet container than your code. I sited the behavior of the IDEs and tutortials from Sun, Oracle and BEA and how they are also inconsistent in whether they close the stream or not.
About IE specific behavior: In our case a separate product 'Oracle Web Cache' was introducing the additional header values which impacts Internet explorer only because of the way IE implements the 'No Cache' requirement (see the MSDN article).
The code is:
public class DownloadServlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException,
IOException {
ServletOutputStream out = null;
ByteArrayInputStream byteArrayInputStream = null;
BufferedOutputStream bufferedOutputStream = null;
try {
response.setContentType("text/csv");
String disposition = "attachment; fileName=data.csv";
response.setHeader("Content-Disposition", disposition);
out = response.getOutputStream();
byte[] blobData = dao.getCSV();
//setup the input as the blob to write out to the client
byteArrayInputStream = new ByteArrayInputStream(blobData);
bufferedOutputStream = new BufferedOutputStream(out);
int length = blobData.length;
response.setContentLength(length);
//byte[] buff = new byte[length];
byte[] buff = new byte[(1024 * 1024) * 2];
//now lets shove the data down
int bytesRead;
// Simple read/write loop.
while (-1 !=
(bytesRead = byteArrayInputStream.read(buff, 0, buff.length))) {
bufferedOutputStream.write(buff, 0, bytesRead);
}
out.flush();
out.close();
} catch (Exception e) {
System.err.println(e); throw e;
} finally {
if (out != null)
out.close();
if (byteArrayInputStream != null) {
byteArrayInputStream.close();
}
if (bufferedOutputStream != null) {
bufferedOutputStream.close();
}
}
}
I am really confused about your "from back through the breast into the head" write mechanism. Why not simple (the servlet output stream will be bufferend, thats container stuff):
byte[] csv = dao.getCSV();
response.setContentType("text/csv");
response.setHeader("Content-Disposition", "attachment; filename=data.csv"));
reponse.setContentLength(csv.length);
ServletOutputStream out = response.getOutputStream();
out.write(csv);
There should also be no need to flush the output stream nor to close.
The header content should not be parsed case sensitive by IE, but who knows: do not camelcase fileName. The next question is the encoding. CSV is text, so you should use getWriter() instead or getOutputStream() and set the content type to "text/csv; charset=UTF-8" for example. But the dao should provide the CSV as String instead of byte[].
The servlet code has nothing to d with HTTPS, so the protocol does not matter from the server side. You may test the servlet from localhost with HTTP i hope.
What about filters in your application? A filter may als set an HTTP header (or as footer) with cache-control for example.