I have a process in a servlet that creates a .pdf file and sends it to the client. However, Adobe won't open the downloaded file ("There was an error opening this document. The file is damaged and could not be repaired."). The original created file residing on the server is fine and Adobe doesn't have a problem opening it.
My code:
private static void sendFile(HttpServletResponse response, String pdfPath) throws FileNotFoundException, IOException {
PrintWriter out = response.getWriter();
File f = new File(pdfPath);
response.setHeader("Content-Transfer-Encoding", "binary");
response.setHeader("Content-Disposition", "attachment; filename=\"" + f.getName());
response.setContentLength((int) f.length());
response.setContentType("application/pdf");
FileInputStream fileInputStream = new FileInputStream(pdfPath);
int i;
while ((i = fileInputStream.read()) != -1) {
out.write(i);
}
fileInputStream.close();
out.close();
}
A Writer writes characters, not bytes.
Use the response output stream.
And don't read and write byte by byte, especially from a FileInputStream.This is extremely inefficient. Just use Files.copy().
Related
I'm currently developping on a webserver for my company , and i'am facing a corrupted zip file problem. At the end of a servlet. Here's the function header called at the very end of the servlet.
sendFile(directory, "s" + this.optimizerId + "-" + this.optimizerName + "-" + start.toDate(Defs.DB.DATE_PATTERN) + "-" + end.toDate(Defs.DB.DATE_PATTERN), FileExtension.NONE, ContentType.ZIP, response);
Here is the first send File function ...
public default void sendFile(File file, String fileName, FileExtension extension, ContentType type, HttpServletResponse response) {
if(type == ContentType.ZIP) {
String[] files = file.list();
if(files != null && files.length > 0) {
byte[] bytes = FileUtils.getZipBytes(file, files);
sendFile(new String(bytes), fileName, extension, type, response);
}
}
}
The "FileUtils.getZipBytes(file, files)" function is doing fine , it returns a byteArray.
I even tried to create a zip file with ZipOutputStream a File , and on the server , the zip has litterally no problem.
( I will send the source code of this function if you think it's important , but i don't want to add useless complexity ).
Then , here's the code of the second sendFile function that is called from the first one.
public default void sendFile(String content, String fileName, FileExtension extension, ContentType type, HttpServletResponse response) {
response.setContentType(type.getExpression());
response.setHeader("Content-Disposition", "attachment; filename=\"" + FileUtils.encodeForFileName(fileName) + "." + extension.getExtension() + "\"");
OutputStream out = response.getOutputStream();
out.write(content.getBytes());
out.flush();
out.close();
}
Do you see any problem in these functions ?
The zip file is downloaded from the server. He is not empty (about 15.7KB).
But when i try to open it , the file explorer of Fedora tell me that the file is corrupted.
I also tried to use the unzip command on the file , and i have the following error message.
Archive: s192-Système U carquefou-2023-01-26 000000-2023-01-26 235959(1).zip
caution: zipfile comment truncated
error [s192-Système U carquefou-2023-01-26 000000-2023-01-26 235959(1).zip]: missing 3249308257 bytes in zipfile
(attempting to process anyway)
error [s192-Système U carquefou-2023-01-26 000000-2023-01-26 235959(1).zip]: attempt to seek before beginning of zipfile
(please check that you have transferred or created the zipfile in the
appropriate BINARY mode and that you have compiled UnZip properly)
I thought it may be the FileUtils.getZipBytes(file, files) that returns a corrupted ByteArray. But i really don't think it is ... Here's the source code of this function
public static byte[] getZipBytes(File directory, String[] files) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos);
byte bytes[] = new byte[2048];
for (String fileName : files) {
FileInputStream fis = new FileInputStream(directory.getPath() +
System.getProperty("file.separator") + fileName);
BufferedInputStream bis = new BufferedInputStream(fis);
zos.putNextEntry(new ZipEntry(fileName));
int bytesRead;
while ((bytesRead = bis.read(bytes)) != -1) {
zos.write(bytes, 0, bytesRead);
}
zos.closeEntry();
bis.close();
fis.close();
}
zos.flush();
baos.flush();
zos.close();
baos.close();
return baos.toByteArray();
}
A zip file is not a string. You should drop the round trip from byte array to string and back to byte array.
Declare your sendFile() method as
public default void sendFile(byte[] content, String fileName, FileExtension extension, ContentType type, HttpServletResponse response) {
response.setContentType(type.getExpression());
response.setHeader("Content-Disposition", "attachment; filename=\"" + FileUtils.encodeForFileName(fileName) + "." + extension.getExtension() + "\"");
OutputStream out = response.getOutputStream();
out.write(content);
out.flush();
out.close();
}
And call it as
sendFile(bytes, fileName, extension, type, response);
The problem with your approach is that new String(content).getBytes() does not return a byte array with the same content as you started with.
I have create Rest Service and I am trying to Generate Zip file. This Zip file created from muliple PDF files which are downloaded using method InputStream inpuStream = new URL(url).openStream() . I am able to Generate Zip file Which included PDF files but PDF files are broken.
Even If i try to Generate it from String its coming as broken PDF and i am getting Error message "Not a supported File Type or file is broken or damaged". Its simple code but seems like i am unable to track the mistake.
I have provided my controller , service method for your reference.
1)Controller:
#GetMapping("/getZipFile")
public void getZipFile(HttpServletResponse response) throws RestException {
try {
ByteArrayOutputStream baos = generateZipService.getZipFile();
ServletOutputStream responseOutPutStream = response.getOutputStream();
response.setContentType("APPLICATION/OCTET-STREAM");
response.setStatus(HttpServletResponse.SC_OK);
response.addHeader("Content-Disposition", "attachment; filename=\"GeneratedZipFile.zip\"");
responseOutPutStream.write(baos.toByteArray());
responseOutPutStream.flush();
} catch (Exception e) {
throw new RestException("Error In downloading Zip File");
}
}
2)Service Method
public ByteArrayOutputStream getZipFile() throws Exception{
List<ZipFileName> zipFileNames= zipFileNameDao.getZipFileName();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zipOut= new ZipOutputStream(baos);
for (String fileName : zipFileNames) {
InputStream inpuStream = new ByteArrayInputStream( "this is test to generarte pdf test file this is test tdfsfs this is test to generarte pdf test file this is test tdfsfs".getBytes(Charsets.UTF_8) );
createZipFile(inpuStream,zipOut,fileName);
inpuStream.close();
}
zipOut.flush();
baos.flush();
zipOut.close();
baos.close();
return baos;
}
3)createzipfile from service method :
```private void createZipFile(InputStream inputStream, ZipOutputStream zipOut,String fileName) throws IOException {
ZipEntry zipEntry = new ZipEntry(fileName+".pdf");
BufferedInputStream bis = new BufferedInputStream(inputStream);
zipOut.putNextEntry(zipEntry);
zipOut.write(IOUtils.toByteArray(inputStream));
zipOut.closeEntry();
bis.close();
inputStream.close();
}
Also , Another question is about using channels. I read channels are better when you have large files to downlaod from server . I have less then 20 kb of file so should I use Java.nio or just Zipoutputstream is fine.
I try with "response.setContentType("APPLICATION/ZIP")" but it didnt change the outcome of the project.
Thank you for your help..
The code worked fine only thing missing was to pass authentication with the openStream() method because of which I was getting the broken PDF. I opened the pdf with notepad++ and found the error ..
I resolved it.
Thank you
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 need to write a code to convert a byte array to ZIP file and make it download in Spring MVC.
Byte array is coming from a webservice which is a ZIP file originally. ZIP file has a folder and the folder contains 2 files. I have written the below code to convert to byte array to ZipInputStream. But I am not able to convert into ZIP file. Please help me in this.
Here is my code.
ZipInputStream zipStream = new ZipInputStream(new ByteArrayInputStream(bytes));
ZipEntry entry = null;
while ((entry = zipStream.getNextEntry()) != null) {
String entryName = entry.getName();
FileOutputStream out = new FileOutputStream(entryName);
byte[] byteBuff = new byte[4096];
int bytesRead = 0;
while ((bytesRead = zipStream.read(byteBuff)) != -1)
{
out.write(byteBuff, 0, bytesRead);
}
out.close();
zipStream.closeEntry();
}
zipStream.close();
I am presuming here that you want to write a byte array to a ZIP file. As the data sent is also a ZIP file and to be saved is also ZIP file, shouldn't be a problem.
Two steps are needed: save it on disk and return the file.
1) Save on disk part:
File file = new File(/path/to/directory/save.zip);
if (file.exists() && file.isDirectory()) {
try {
OutputStream outputStream = new FileOutputStream(new File(/path/to/directory/save.zip));
outputStream.write(bytes);
outputStream.close();
} catch (IOException ignored) {
}
} else {
// create directory and call same code
}
}
2) Now to get it back and download it, you need a controller :
#RequestMapping(value = "/download/attachment/", method = RequestMethod.GET)
public void getAttachmentFromDatabase(HttpServletResponse response) {
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=\"" + file.getFileName() + "\"");
response.setContentLength(file.length);
FileCopyUtils.copy(file as byte-array, response.getOutputStream());
response.flushBuffer();
}
I have edited the code I have, so you will have to make some changes before it suits you 100%. Let me know if this is what you were looking for. If not, I will delete my answer. Enjoy.
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");
}