I m using content-disposition to download pdf . When I click the download button, the complete pdf file is downloaded first and then browser shows the dialog box to save the file. I want the browser to show the process of downloading. The following is my servlet code:
String filename = "abc.pdf";
String filepath = "/pdf/" + filename;
resp.setContentType("application/pdf");
resp.addHeader("content-disposition", "attachment; filename=" + filename);
ServletContext ctx = getServletContext();
InputStream is = ctx.getResourceAsStream(filepath);
System.out.println(is.toString());
int read = 0;
byte[] bytes = new byte[1024];
OutputStream os = resp.getOutputStream();
while ((read = is.read(bytes)) != -1) {
os.write(bytes, 0, read);
}
System.out.println(read);
os.flush();
os.close();
}catch(Exception ex){
logger.error("Exception occurred while downloading pdf -- "+ex.getMessage());
System.out.println(ex.getStackTrace());
}
The progress cannot be determined without knowing the response body's content length beforehand in the client side. To let the client know about the content length, you need to set the Content-Length header in the server side.
Change the line
InputStream is = ctx.getResourceAsStream(filepath);
to
URL resource = ctx.getResource(filepath);
URLConnection connection = resource.openConnection();
response.setContentLength(connection.getContentLength()); // <---
InputStream is = connection.getInputStream();
// ...
Unrelated to the concrete problem, your exception handling is bad. Replace the line
System.out.println(ex.getStackTrace());
by
throw new ServletException(ex);
Related
I am trying to download a PDF file with HttpClient, it is downloading the PDF file but pages are blank. I can see the bytes on console from response if I print them. But when I try to write it to file it is producing a blank file.
FileUtils.writeByteArrayToFile(new File(outputFilePath), bytes);
However the file is showing correct size of 103KB and 297KB as expected but its just blank!!
I tried with Output stream as well like:
FileOutputStream fileOutputStream = new FileOutputStream(outFile);
fileOutputStream.write(bytes);
Also tried to write with UTF-8 coding like:
Writer out = new BufferedWriter( new OutputStreamWriter(
new FileOutputStream(outFile), "UTF-8"));
String str = new String(bytes, StandardCharsets.UTF_8);
try {
out.write(str);
} finally {
out.close();
}
Nothing is working for me. Any suggestion is highly appreciated..
Update: I am using DefaultHttpClient.
HttpGet httpget = new HttpGet(targetURI);
HttpResponse response = null;
String htmlContents = null;
try {
httpget = new HttpGet(url);
response = httpclient.execute(httpget);
InputStreamReader dataStream=new InputStreamReader(response.getEntity().getContent());
byte[] bytes = IOUtils.toByteArray(dataStream);
...
You do
InputStreamReader dataStream=new InputStreamReader(response.getEntity().getContent());
byte[] bytes = IOUtils.toByteArray(dataStream);
As has already been mentioned in comments, using a Reader class can damage binary data, e.g. PDF files. Thus, you should not wrap your content in an InputStreamReader.
As your content can be used to construct an InputStreamReader, though, I assume response.getEntity().getContent() returns an InputStream. Such an InputStream usually can be directly used as IOUtils.toByteArray argument.
So:
InputStream dataStream=response.getEntity().getContent();
byte[] bytes = IOUtils.toByteArray(dataStream);
should already work for you!
Here is a method I use to download a PDF file from a specific URL. The method requires two string arguments, an url string (example: "https://www.ibm.com/support/knowledgecenter/SSWRCJ_4.1.0/com.ibm.safos.doc_4.1/Planning_and_Installation.pdf") and a destination folder path to download the PDF file (or whatever) into. If the destination path does not exist within the local file system then it is automatically created:
public boolean downloadFile(String urlString, String destinationFolderPath) {
boolean result = false; // will turn to true if download is successful
if (!destinationFolderPath.endsWith("/") && !destinationFolderPath.endsWith("\\")) {
destinationFolderPath+= "/";
}
// If the destination path does not exist then create it.
File foldersToMake = new File(destinationFolderPath);
if (!foldersToMake.exists()) {
foldersToMake.mkdirs();
}
try {
// Open Connection
URL url = new URL(urlString);
// Get just the file Name from URL
String fileName = new File(url.getPath()).getName();
// Try with Resources....
try (InputStream in = url.openStream(); FileOutputStream outStream =
new FileOutputStream(new File(destinationFolderPath + fileName))) {
// Read from resource and write to file...
int length = -1;
byte[] buffer = new byte[1024]; // buffer for portion of data from connection
while ((length = in.read(buffer)) > -1) {
outStream.write(buffer, 0, length);
}
}
// File Successfully Downloaded");
result = true;
}
catch (MalformedURLException ex) { ex.printStackTrace(); }
catch (IOException ex) { ex.printStackTrace(); }
return result;
}
I am generating csv in my code, It takes some time to generate. So, I am sending an email with link once the csv file is generated. When I click that, getting 404 not found error. When I have the same link in the html, I am able to download it. Any insight or sample to refer
Sample Link -http://localhost:9090/api/report/file?fileName=filename.csv
Java code to download the report
#RequestMapping(value = "api/report/file")
public void downloadCSV(HttpServletResponse response, #RequestParam("fileName") String fileName) throws IOException {
File file = new File(fileName);
InputStream is = new FileInputStream(file);
response.setContentType("application/octet-stream");
// Response header
response.setHeader("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"");
// Read from the file and write into the response
OutputStream os = response.getOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
os.flush();
os.close();
is.close();
}
Add GET method to this mapping: #RequestMapping(value = "api/report/file")
I am using the below code invoked via ajax call for create my excel file.On click the download button excel file gets generate at the root location. But I am not able to see the prompt for user to save/save as the file. I can see the response tab of browser where the content of excel are coming. But I want that Save As dialog option to come. Any correction in below code that can solve this issue?
final Date date = new Date();
final String generateDate= new SimpleDateFormat("yyyy-MM-dd").format(date);
final String filename = form_name+"-extraction-"+generateDate.toString()+".xls";
final FileOutputStream fileOutputStream = new FileOutputStream(filename);
workbook.write(fileOutputStream);
downloadFile(filename, response);
Below is download file method :
private void downloadFile(final String fileName, final SlingHttpServletResponse response){
try {
final File f = new File(fileName);
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-Disposition", "inline; filename="+fileName);
response.setHeader("Pragma", "public");
response.setHeader("Cache-Control", "no-store");
response.addHeader("Cache-Control", "max-age=0");
FileInputStream fin = null;
try {
fin = new FileInputStream(f);
} catch (final FileNotFoundException e) {
e.printStackTrace();
}
final int size = 1024;
try {
response.setContentLength(fin.available());
final byte[] buffer = new byte[size];
ServletOutputStream os = null;
os = response.getOutputStream();
int length = 0;
while ((length = fin.read(buffer)) != -1) {
os.write(buffer, 0, length);
}
fin.close();
os.flush();
os.close();
} catch (final IOException e) {
e.printStackTrace();
}
}catch (final Exception ex){
LOGGER.error("ERROR IS ::: {}",ex.getMessage());
}
If the disposition type matches "attachment" (case-insensitively),
this indicates that the recipient should prompt the user to save the
response locally, rather than process it normally (as per its media
type).
On the other hand, if it matches "inline" (case-insensitively),
this implies default processing. Therefore, the disposition type
"inline" is only useful when it is augmented with additional
parameters, such as the filename (see below).
Unknown or unhandled disposition types SHOULD be handled by
recipients the same way as "attachment" (see also [RFC2183],
Section 2.8).
So you are forcing the browser to show the file instead of downloading it with your
response.setHeader("Content-Disposition", "inline; filename="+fileName);
call. You should use "attachment" instead of "inline".
response.setHeader("Content-Disposition", "attachment; filename="+fileName);
Read Use of the Content-Disposition Header Field in the Hypertext Transfer Protocol (HTTP) for more info.
If previous searched data output is populated in Excel, you should remove static declarations. For example:
private static hasmap hs = new hashmap();
<%
Document downloadFile = null;
String mimeType = null;
try{
downloadFile = new DocumentsDao().loadById(Long.parseLong(request.getParameter("id")));
// gets MIME type of the file
mimeType = downloadFile.getFileType();
if (mimeType == null) {
// set to binary type if MIME mapping not found
mimeType = "application/octet-stream";
}
System.out.println("MIME type: " + mimeType);
}catch (Exception e){
return;
}
// modifies response
response.reset();
response.resetBuffer();
response.setContentType(mimeType);
response.setContentLength((int) downloadFile.getDocumentData().length);
// forces download
String headerKey = "Content-Disposition";
String headerValue = String.format("attachment; filename=\"%s\"", downloadFile.getFileName());
response.setHeader(headerKey, headerValue);
// obtains response's output stream
OutputStream outStream = response.getOutputStream();
byte[] buffer = new byte[8192];
int bytesRead = -1;
System.out.println("### Length from db = "+downloadFile.getDocumentData().length);
ByteArrayInputStream inStream = new ByteArrayInputStream(downloadFile.getDocumentData());
while ((bytesRead = inStream.read(buffer)) != -1) {
outStream.write(buffer, 0, bytesRead);
}
inStream.close();
outStream.close();
response.flushBuffer();
return;
%>
The above code in a JSP produces a file to download which has an additional sequence of 0d0a x 4 at the end which causes the microsoft applications word and excel to complain and have to repair the file which has been downloaded.
I thought it might be the upload of the file but it was not, and retrieving from the database is fine. So the input stream is fine the problem occurs after the output stream is closed.
Errors you get are 'Word found unreadable content' 'Excel found unreadable content'
Has anyone seen this?
cheers
Charlie
The conversion of the JSP to servlet introduced the 0d0a X 4 characters. I used fiddle to find that the Apache Tomcat web server was altering the content length and sending the bytes. I looked at working apps at work and they all used servlets to do the job so I converted the above code to a servlet and it worked perfectly.
So don't use JSPs for this purpose.
I have a method which start download of text file that is stored in oracle.
The column type is BLOB. I'm using this code below to init download, but I have no idea how set encode to this file when client downloaded it.
if (result.next()) {
String fileName0 = String.valueOf(result.getDate(columnData));
String fileName1 = String.valueOf(result.getInt(columnNumSolit));
String fileName2 = String.valueOf(result.getInt(columnNumComplto));
BLOB blob = ((OracleResultSet) result).getBLOB(columnFile);
InputStream inputStream = blob.getBinaryStream();
//int fileLength = inputStream.available();
int fileLength = blob.getChunkSize();
ServletContext context = getServlet().getServletContext();
// Set MIME to file.
String mimeType = context.getMimeType(fileName0+fileName1+fileName2+ext);
if (mimeType == null) {
mimeType = "application/octet-stream";
}
// header to response.
response.setContentType(mimeType);
response.setContentLength(fileLength);
String headerKey = "Content-Disposition";
String headerValue = String.format("attachment; filename=\"%s\"",fileName0+fileName1+fileName2+ext);
response.setHeader(headerKey, headerValue);
// write file to client.
OutputStream outStream = response.getOutputStream();
byte[] buffer = new byte[fileLength];
int bytesRead = -1;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outStream.write(buffer, 0, bytesRead);
}
inputStream.close();
outStream.close();
tipoSolit = null;
}else {
//do something
}
What behavior are you trying to get the browser to do? Are you sending a video or mp3 or what? The mimetype is set by the headers you send before the file. If know of a website that has the behavior you are looking for, you can simply view what headers they send in chrome or in wireshark and then just use those in your own code.
One of the problems with file-type encoding is that you need to keep the encoding correct for each step that the bytes take. This means that if any step in the "upload->storage in db->fetching db->download" removes utf-8 encoding, you get the wrong answer. I wrote a post about my travels with UTF-8 and MySQL here: https://stackoverflow.com/a/14411280/836450 I would suggest sending a file that has a single utf-8 character, and then debugging the entire chain by hand.