I want to open my servlet which basically checks my permissions and if allowed, shows me a picture like it would be a real jpeg. My code so far:
File file = new File("C:/test.jpeg");
FileInputStream fis = new FileInputStream(file);
byte imageBytes[] = new byte[(int) file.length()];
response.setContentType("image/jpeg");
response.setContentLength(imageBytes.length);
response.getOutputStream().write(imageBytes);
response.getOutputStream().flush();
But somehow the image it tries to show me in the browser is corrupted. I already checked that the file exists, as the image.length is not zero. What did I do wrong?
You should start using the newer NIO.2 File API that was added in Java 7, mostly the Files class, because it makes the job much easier.
Option 1: Load file into byte[]
byte[] imageBytes = Files.readAllBytes(Paths.get("C:/test.jpeg"));
response.setContentType("image/jpeg");
response.setContentLength(imageBytes.length);
try (OutputStream out = response.getOutputStream()) {
out.write(imageBytes);
}
Option 2: Stream the file without using a lot of memory (recommended)
Path imageFile = Paths.get("C:/test.jpeg");
response.setContentType("image/jpeg");
response.setContentLength((int) Files.size(imageFile));
try (OutputStream out = response.getOutputStream()) {
Files.copy(imageFile, out);
}
Related
I'm trying to create a zip file to be able to send multiple files over http.
My issue is that the Zip file that is generated is "corrupted" before and after the file has been send. The issue is i'm not able to find what i did wrong as i'm getting no errors inside the console.
So does someone has an idea file my generated zip file is corrupted ?
This is my code :
OutputStream responseBody = t.getResponseBody();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos);
int counter = 1;
for (PDDocument doc : documents)
{
ZipEntry zipEntry = new ZipEntry("document" + counter);
zos.putNextEntry(zipEntry);
ByteArrayOutputStream docOs = new ByteArrayOutputStream();
doc.save(docOs);
docOs.close();
zos.write(docOs.toByteArray());
zos.closeEntry();
zos.finish();
zos.flush();
counter++;
}
zos.close();
baos.close();
responseBody.write(baos.toByteArray());
responseBody.flush();
Thank you for your help !
You need to remove zos.finish() from inside the loop as it terminates the ZIP entries, as it is handled by zos.close() at end of the stream.
With very large streams you will be better off sending ZIP directly to responseBody bypassing ByteArrayOutputStream memory buffer.
If you are still having problems check the content type of the output is set. It might be easier to debug by temporarily writing the byte[] to file to check the ZIP format you are sending with:
Files.write(Path.of("temp.zip"), baos.toByteArray());
This outline below shows sending a simple ZIP over http (from a servlet, adjust the first 2 lines to appropriate calls for "t"). This may help you check which step of your code causes the corruption if you work back to adding your own document objects inside the loop:
// MUST set response content type:
// resp.setContentType("application/zip");
OutputStream out = resp.getOutputStream(); // or t.getResponseBody();
try(ZipOutputStream zos = new ZipOutputStream(out))
{
while (counter-- > 0)
{
ZipEntry zipEntry = new ZipEntry("document" + counter+".txt");
zos.putNextEntry(zipEntry);
zos.write(("This is ZipEntry: "+zipEntry.getName()+"\r\n").getBytes());
}
}
The Zip file output from using ByteArrayOuputStream is smaller than that with FileOutputStream.
In Java I am creating a Zip file. Which opens successfully when I use FileOutputStream.
When switching the FileOutputStream to a ByteArrayOutputStream I get a slightly smaller file, about 221 bytes. And this file won't open, but has the same contents as the other but missing 221 bytes.
The logic I use to construct the Zip file is the same in both cases. Therefore I will share the essential logic without the zip creation (let me know if you need this).
ByteArrayOutputStream baos = new ByteArrayOutputStream();
zipOS = new ZipOutputStream(baos);
Then I create the Zip file, and then ...
zipOS.flush();
baos.flush();
baos.writeTo(new FileOutputStream("TEST_AS_BYTESTREAM_1.zip"));
zipOS.close();
baos.close();
I'm using the baos.writeTo() to bypass this to prove it's not the HTTP response but the ByteArrayOutputStream element thats an issue.
When using baos.writeTo(new FileOutputStream("TEST_AS_BYTESTREAM_1.zip"));
I get a smaller file that doesn't open as a Zip.
This logic is in a java controller so that a link clicked on web page will down load the zip file without touching any file systems excepted the users.
Here is the code using FileOutputStream which works...
FileOutputStream baos = new FileOutputStream("TEST_AS_FILE.zip");
zipOS = new ZipOutputStream(baos);
Aside from the the second code snippet isn't relevant as I would need to send the file created on the web server. But the point is, that Fileoutput stream with the same logic and ByteArrayOutputStream has a difference.
The following statement is wrong:
baos.writeTo(new FileOutputStream("TEST_AS_BYTESTREAM_1.zip"));
The javadoc of writeTo(OutputStream out) does not say anything about closing the OutputStream, which means it doesn't, so the last of the data is still sitting buffered in the un-closed, un-flushed FileOutputStream.
The correct way is:
try (OutputStream out = new FileOutputStream("TEST_AS_BYTESTREAM_1.zip")) {
baos.writeTo(out);
}
Also, a ByteArrayOutputStream does not need to be closed or flushed. As the javadoc of close() says it:
Closing a ByteArrayOutputStream has no effect.
You will however need to call finish() on an ZipOutputStream to complete the content. Since closing the zip stream automatically finishes it for you, the correct way to do this is:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ZipOutputStream zipOS = new ZipOutputStream(baos)) {
// create the Zip file content here
}
try (OutputStream out = new FileOutputStream("TEST_AS_BYTESTREAM_1.zip")) {
baos.writeTo(out);
}
When writing directly to a file:
try (ZipOutputStream zipOS = new ZipOutputStream(new FileOutputStream("TEST_AS_FILE.zip"))) {
// create the Zip file content here
}
Consider adding a BufferedOutputStream for better performance.
The final answer is as follows. Many thanks to the answer previously given.
Basically I needed to use the .finish() method of the ZipOuputStream. Everything else remained the same.
Pre zip creation code same as before
ByteArrayOutputStream baos = new ByteArrayOutputStream();
zipOS = new ZipOutputStream(baos);
Post zip creation...
zipOS.flush();
zipOS.finish();
baos.flush();
documentBody = baos.toByteArray();
zipOS.close();
baos.close();
Then my HTTP response is
HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType("application", "zip"));
headers.setContentLength(documentBody.length);
headers.add("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
ByteArrayResource resource = new ByteArrayResource(documentBody);
return ResponseEntity.ok()
.headers(headers)
.body(resource);
This way I avoid the creation of a file which is nice.
Many thanks for the assistance.
I am getting byte array from web service. this byte array is the pdf file. Below code execute well and download file on browser. But this file is seems corrupt. Also additional copy of file gets created on server which I am trying to avoid.
byte[] rawFile = myService.getDocument(param1, param2);
try (BufferedInputStream in = new BufferedInputStream(new ByteArrayInputStream(rawFile));
FileOutputStream fileOutputStream = new FileOutputStream("myfile-1.pdf")) {
byte dataBuffer[] = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) {
fileOutputStream.write(dataBuffer, 0, bytesRead);
}
response.setContentType("application/pdf");
response.setHeader("Content-Disposition","attachment;filename=myfile-1.pdf");
response.flushBuffer();
} catch (final Exception ex) {
ex.printStackTrace();
}
}
In a nutshell, below are 2 issue.
Downloaded file (on browser) is seems corrupt and not open. Generic pdf error message appears.
File which created on server is opening fine and shows content. But this file should not be physically present on server.
Downloaded file (on browser) is seems corrupt and not open.
Because you never sent the file content to the browser.
this file should not be physically present on server.
Then why did you explicitly write it there using FileOutputStream?
You need to write the file content to the response.
byte[] rawFile = myService.getDocument(param1, param2);
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment;filename=myfile-1.pdf");
OutputStream out = response.getOutputStream();
out.write(rawFile);
// no need to close or flush, that happens automatically when you return
I need to save a file and download file in directory outside server context.
I am using Apache Tomacat
I am able to do this in directory present in webapps directory of application
If my directory structure is as follows,
--src
--WebContent
-- uploaddir
-- myfile.txt
Then I am able to download in by simply.
download
But, problem is when file is in some other directory say d:\\uploadedfile\\myfile.txt
then I wont be able to download it, as resource is not in server context as above.
I have file path to uuid mapping,
like,
d:\\uploadedfiles\\myfile.txt <-> some_uuid
then I want file should be downloaded, on click of following,
download
So, How to make file downloadable when it is outside the server context,
I heard about getResourceAsStream() method which would do this , But would any one help me on how to do this, probably with simple code snippet?
Try the below code which you can write in filedownloadservet. Fetch the file name from the request parameter and then read and write the file.
If you need to do some security checks then do that before processing the request.
File file = new File("/home/files", "file name which user wants to download");
response.setContentType(getServletContext().getMimeType(file.getName()));
response.setContentLength(file.length());
BufferedInputStream inputStream = null;
BufferedOutputStream outputStream = null;
try {
inputStream = new BufferedInputStream(new FileInputStream(file));
outputStream = new BufferedOutputStream(response.getOutputStream());
byte[] buf = new byte[2048];
int len;
while ((len = inputStream.read(buf)) > 0) {
outputStream.write(buf, 0, len);
}
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
//log it
}
}
// do the same for input stream also
}
here i found the answer,
response.setContentType("application/msword");
response.setHeader("Content-Disposition","attachment;filename=downloadname.doc");
File file=new File("d:\\test.doc");
InputStream is=new FileInputStream(file);
int read=0;
byte[] bytes = new byte[BYTES_DOWNLOAD];
OutputStream os = response.getOutputStream();
while((read = is.read(bytes))!= -1){
os.write(bytes, 0, read);
}
os.flush();
os.close();
Base path will not work that is for HTML and it works if the base path is also exposed by your web server which does not look like case here.
To download an arbitary file you need to open the file using a FileInputStream (and surround it by a buffered input stream), read a byte, then send that byte from your servlet to the client.
Then there are security concerns, so should google that (basically not give access to any file but only file that is to be shared, audit download etc as needed.
Again in your servlet set the mime type etc and then open a input stream and write the bytes to the output stream to client
I am trying to read multiple files (can be of any format i.e. pdf, txt, tiff etc) from URLs and zipping them using ZipOutputStream. My code looks like this:
// using in-memory file read
// then zipping all these files in-memory
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos);
.....
URL url = new URL(downloadUrl); // can be multiple URLs
ByteArrayOutputStream bais = new ByteArrayOutputStream();
InputStream is = url.openStream();
byte[] byteChunk = new byte[4096];
int n;
while ( (n = is.read(byteChunk)) > 0 )
{
bais.write(byteChunk, 0, n);
}
byte[] fileBytes = bais.toByteArray();
ZipEntry entry = new ZipEntry(fileName);
entry.setSize(fileBytes.length);
zos.putNextEntry(entry);
zos.write(fileBytes);
zos.closeEntry();
// close the url input stream
is.close();
// close the zip output stream
zos.close();
// read the byte array from ByteArrayOutputStream
byte[] zipFileBytes = baos.toByteArray();
String fileContent = new String(zipFileBytes);
I am then passing this content "fileContent" to my perl frontend application.
And I m using perl code to download this zipped file:
WPHTTPResponse::setHeader( 'Content-disposition', 'attachment; filename="test.zip"' );
WPHTTPResponse::setHeader( 'Content-type', 'application/zip');
print $result; // string coming from java application
But the zip file it is giving is corrupted. I think something in going wrong with the data translation.
I'd appreciate any help.
Your problem is thinking that you can output your zip bytes into a string. This string can not be used to reproduce the zip content again. You need to either use the raw bytes or encode the bytes into something that can be represented as a string, such as base64 encoding.