here i'm trying to read pdf file from server using java servlet. the below code i'm getting file path if file exists and then try to read file but,file does not open ?
String filePath = dirName;
String fileName = si + "_" + dnldFilename;
FileInputStream fileToDownload = new FileInputStream(filePath.concat(fileName);
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
response.setContentLength(fileToDownload.available());
int c;
while ((c = fileToDownload.read()) != -1) {
response.getOutputStream().write(c);
}
response.getOutputStream().flush();
response.getOutputStream().close();
fileToDownload.close();
The bug is here:
response.setContentLength(fileToDownload.available());
The InputStream#available() doesn't do what you (and the average Java starter) think. It doesn't return the total content length which is what the response header represents. It returns the amount of bytes available for reading without blocking all other threads (i.e. bytes which are currently already put in hardware buffer). If this is lower than the actual file size, then the client may simply stop reading the remainder of the response. In other words, the client got only a part of the PDF file, because you told the client that the desired part is of exactly the given length.
You need to construct a normal java.io.File and get the file size via File#length().
Here's a complete rewrite, reducing further legacy Java IO boilerplate too. This assumes you're on at least Java 7 (as Java 6 is EOL since 2013, no one would expect you're 2 years after date still at Java 6 anyway):
File file = new File(filePath, fileName);
response.setHeader("Content-Type", getServletContext().getMimeType(file.getName()));
response.setHeader("Content-Length", String.valueOf(file.length()));
response.setHeader("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"");
Files.copy(file.toPath(), response.getOutputStream());
That's it. Do note that the above code snippet is this way reusable for all other kinds of files too. You can manage unknown content types via <mime-mapping> entries in web.xml.
Also note that I'm converting the content length to string as the setContentLength() takes only an int and File#length() returns long, which would fail if the file is larger than 2GB. In case you're on Servlet 3.1 (Tomcat 8, etc) already, make use of new setContentLengthLong() method.
response.setContentLengthLong(file.length());
See also:
Simplest way to serve static data from outside the application server in a Java web application
Abstract template for static resource servlet
Related
I am currently developing a REST service which receives in its request a field where it is passed a file in base 64 format ("n" characters come). What I do within the service logic is to convert that character string to a File to save it in a predetermined path.
But the problem is that when the file is too large (3MB) the service becomes slow and takes a long time to respond.
This is the code I am using:
String filename = "TEXT.DOCX"
BufferedOutputStream stream = null;
// THE FIELD base64file IS WHAT A STRING IN BASE FORMAT COMES FROM THE REQUEST 64
byte [] fileByteArray = java.util.Base64.getDecoder (). decode (base64file);
// VALID FILE SIZE
if ((1 * 1024 * 1024 <fileByteArray.length) {
logger.info ("The file [" + filename + "] is too large");
} else {
stream = new BufferedOutputStream (new FileOutputStream (new File ("C: \" + filename)));
stream.write (fileByteArray);
}
How can I do to avoid this inconvenience. And that my service does not take so long to convert the file to File.
Buffering does not improve your performance here, as all you are trying to do is simply write the file as fast as possible. Generally it looks fine, change your code to directly use the FileOutputStream and see if it betters things:
try (FileOutputStream stream = new FileOutputStream(path)) {
stream.write(bytes);
}
Alternatively you could also try using something like Apache Commons to do the task for you:
FileUtils.writeByteArrayToFile(new File(path), bytes);
Try the following, also for large files.
Path outPath = Paths.get(filename);
try (InputStream in = Base64.getDecoder ().wrap(base64file)) {
Files.copy(in, outPath);
}
This keeps only a buffer in memory. Your code might become slow because of taking more memory.
wrap takes an InputStream which you should provide, not the entire String.
From Network point of view:
Both json and xml can support large amount of data exchange. And, 3MB is not really huge. But, there is a limitation on how much browser can handle (if this call is from a user interface).
Also, web server like Tomcat has property to handle 2MB by default (check maxPostSize http://tomcat.apache.org/tomcat-6.0-doc/config/http.html#Common_Attributes)
You can also try chunking the request payload (although it shouldn't be required for a 3MB file)
From Implementation point of view:
Write operation on your disk could be slow. It also depends on your OS.
If your file size is really large, you can use Java FileChannel class with ByteBuffer.
To know the cause of slowness (network delay or code), check the performance with a simple Java program against the web service call.
I want to allow end-users of my site to download files from the server,
I tried to use the classic method using 2 jsp files :
index.jsp :
download the file
download.jsp :
<%
String filename = "file.xls";
String filepath = "C:\\Files\\";
response.setContentType("APPLICATION/OCTET-STREAM");
response.setHeader("Content-Disposition","attachment; filename=\"" + filename + "\"");
java.io.FileInputStream fileInputStream=new java.io.FileInputStream(filepath + filename);
int i;
while ((i=fileInputStream.read()) != -1) {
out.write(i);
}
fileInputStream.close();
%>
But, it's not working with 2 Page Template in Fatwire 7.6.2,
Is that because I am not allowed to use reponse object in Fatwire ?
Using response object within a Sites (aka "fatwire") jsp is indeed discouraged. The typical way to make files available for download in Sites is to model the data in an asset, then use blobserver tags to render a url. See http://docs.oracle.com/cd/E29542_01/apirefs.1111/e39371/JSP/render-getbloburl.html for examples and other similar tags.
If you don't want to put these files into assets, then you may be better off not using blobserver tags and simply making them available directly through the webserver.
Phil
Hi all and thanks for taking the time to answer my question.
I'm trying to send an array of bytes to the client so that his browser can reconstruct in e PDF file. Below is my code:
OutputStream out = response.getPortletOutputStream();
response.setProperty("Content-Disposition", "attachment; filename=" + fileName + ".pdf");
response.setContentType("application/pdf");
out.write(pdfInvoice);
out.flush();
out.close();
We're working with Liferay Portlets but that should not make a difference. pdfInvoice is the byte array. Nothing happens when this code executes. Can you spot what's wrong? Thanks in advance!
You can't serve a pdf in a Portlet response like that, the standard approach is to create a servlet which serves the PDF to the client.
If you want to serve the PDF inside a Portlet this guide should help http://www.liferay.com/community/wiki/-/wiki/Main/Generate+PDF+File+in+Portlet
In my project (Java SpringMVC3) I get an XLS file via HttpClient and I want that file to be downloaded like it's a real download. A popup window showing download dialog.
How can I do that?
Controller should copy the content of file to response object. Do not forget - controller function must return NULL. Below I show a working example from my application:
String filename = /* path to a file */
File file = new File(filename);
response.setContentType(new MimetypesFileTypeMap().getContentType(file));
response.setContentLength((int)file.length());
response.setHeader("content-disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));
InputStream is = new FileInputStream(file);
FileCopyUtils.copy(is, response.getOutputStream());
return null;
Basically you need to implement a Controller that takes care of the download and specify the response's header-mime type. then you invoke that Controller from the view.
Here is a short example how to specify a header-mime type
HTTP Header Mime Type in Websphere Application Server 7
I have a jsp written in which i am downloading certain files... they are pdf, zip, ppt and wmv. All the file types works except wmv. I couldnt figure out problem. When i play wmv file i get following error.
Windows Media Player cannot play the file. The Player might not support the file type or might not support the codec that was used to compress the file.
In my jps i have written code as following
response.setContentType("video/x-ms-wmv");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition", "attachment; filename=123.wmv;");
String fileName = "/logs/164266828.wmv";
FileInputStream input = new FileInputStream(fileName);
BufferedInputStream buf = new BufferedInputStream(input);
int readBytes = 0;
ServletOutputStream myOut = response.getOutputStream( );
while((readBytes = buf.read( )) != -1)
myOut.write(readBytes);
Any inputs or modifications would be of great help !!!
Do not use JSP to stream binary data. JSP may have corrupted it with template text (whitespace and so on outside those <% %> things). Move this code to the doGet() method of a servlet class and invoke the servlet instead of JSP.
Unrelated to the concrete problem, those files seems static files. You don't necessarily need a servlet for this if you have full control over your server. In case of for example Tomcat, you could add the folder with the static files as another <Context> to the server.xml file.
See also:
Reliable data serving
Simplest way to serve static data from outside the application server in a Java web application