Closing GZIPOutputStream after ByteStream copy in finally block break zip - java

I have code which Zip and Unzip Text. I'm facing weird behavior - only when I close the GZIPOutputStream in certain place, the code works but when I try to place the GZIPOutputStream close in the finally block, it breaks and does not work. If you place the : gzipoutputstream.close() in the czip function where comment is placed, it will work. However if you keep it only in finally block, it will break. Why?
the ByteStreams.copy function is from com.google.common.io guava
public class Main {
public static byte[] dzip(byte[] s) throws IOException {
ByteArrayInputStream sStream = null;
ByteArrayOutputStream oStream = null;
InputStream gzipoutputStream = null;
ByteBuffer arrReturn = null;
try {
sStream = new ByteArrayInputStream(s);
oStream = new ByteArrayOutputStream();
gzipoutputStream = new GZIPInputStream(sStream, 1024);
ByteStreams.copy(gzipoutputStream, oStream);
arrReturn = ByteBuffer.wrap(oStream.toByteArray());
}
catch (Exception e) {
return null;
} finally {
if (gzipoutputStream != null) {
gzipoutputStream.close();
}
if (oStream != null) {
oStream.close();
}
if (sStream != null) {
sStream.close();
}
}
return arrReturn.array();
}
public static byte[] czip(byte[] s) throws IOException {
ByteArrayInputStream sStream =null;
ByteArrayOutputStream oStream =null;
OutputStream gzipoutputstream = null;
byte[] returnValue = null;
try {
sStream = new ByteArrayInputStream(s);
oStream = new ByteArrayOutputStream(s.length / 2);
gzipoutputstream = new GZIPOutputStream(oStream, 1024);
ByteStreams.copy(sStream, gzipoutputstream);
//////////// ------------------------------------ \\\\\\\\\\\\
//gzipoutputstream.close(); // < --- Works only when placed here
//////////// ------------------------------------ \\\\\\\\\\\\
returnValue = oStream.toByteArray(); // Here the zip is 000
}catch(Exception e) {
return null;
} finally {
if (gzipoutputstream != null) {
gzipoutputstream.close();
}
if (oStream != null) {
oStream.close();
}
if (sStream != null) {
sStream.close();
}
}
return returnValue;
}
public static void main(String[] args) throws IOException {
String s = "12313dsfafafafaf";
byte[] source = (byte[]) s.getBytes();
byte[] returnZipByteArr = czip(source);
byte[] dd = dzip(returnZipByteArr);
String ss = new String(dd);
System.out.println(ss);
}
}

That's expected. The javadoc of close() says:
Writes remaining compressed data to the output stream and closes the underlying stream.
So if you try to access the bytes in the ByteArrayOutputStream() before close() has been called, the gzip compression hasn't been finished yet: the GZIP stream needs to know that nothing will never be written again to properly write the remaining data.
You could call finish() to have the same effect but without closing the stream (and thus still close it in the finally block).

Related

Create and edit a File object in java without physically writing to hard disc

I am trying to create and edit a text file object in java. But once I execute the code, it is physically writing the file in root folder from which the program being executed. How can I do the same without physically writing the text file to hard disc?
File file = new File("tempFile.txt"); // Now the file is not created physically
writeIntoFile(file, listOfString); // After this, the file is created in disc
private static void writeIntoFile(File file, List<String> contents) {
Writer output;
try {
output = new BufferedWriter(new FileWriter(file.getPath(), true));
for (String content : contents) {
output.append(content);
output.append("\r\n");
}
output.close();
} catch (Exception e) {
e.printStackTrace();
}
}
Thanks #Gerome Tahud. The below code did the trick!
private static byte[] writeIntoFile(List<String> contents) {
try {
if (null != contents && contents.size() > 0) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (String content : contents) {
content = content + "\r\n";
InputStream inputStream = new ByteArrayInputStream(content.getBytes());
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
baos.write(buffer, 0, bytesRead);
}
}
return baos.toByteArray();
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}

How can i zip multiple pdf files in Primefaces?

I want to zip multiple pdf files which are selected in the data-table and let the user download them.
Here is XHTML;
<p:commandLink id="print_orders"
value="Print Selected Orders" ajax="false"
onclick="PrimeFaces.monitorDownload(startPrint, stopPrint);"
styleClass="button button--ujarak button--border-thin button--text-medium download"
style="text-align: center; float:none; margin: 0px auto 0px auto; padding: 0.05em 0.1em;" >
<p:fileDownload value="#{printOrdersManagedBeanSAP.printsAction()}" />
</p:commandLink>
Let me clarify managedbean side;
purchaseOrder object includes PO_NUMBER() I generate pdf document (pdfDoc) as ByteArrayOutputStream from SAP with PO_NUMBER(). With for loop I tried to produce zip file includes pdf documents as much as the selected column. By the way I'm not sure I did it right.
With "return (StreamedContent) output;" code block I tried to return zip file but I get "java.util.zip.ZipOutputStream cannot be cast to org.primefaces.model.StreamedContent" exception. I tried to convert ZipOutputStream to StreamedContent because of <p:fileDownload> Primefaces tag.
Can you help me with how to fix this problem?
public StreamedContent printsAction()
{
if(!termsAgreed)
RequestContext.getCurrentInstance().execute("PF('warningDialog').show();");
else
{
if (getSelectedPurchaseOrders() != null && !getSelectedPurchaseOrders().isEmpty()) {
try
{
FileOutputStream zipFile = new FileOutputStream(new File("PO_Reports.zip"));
ZipOutputStream output = new ZipOutputStream(zipFile);
for (PurchaseOrderSAP purchaseOrder : getSelectedPurchaseOrders()) {
ByteArrayOutputStream pdfDoc = purchaseOrderSAPService.printOrder(selectedPurchaseOrder.getPO_NUMBER());
ZipEntry zipEntry = new ZipEntry(purchaseOrder.getPO_NUMBER());
output.putNextEntry(zipEntry);
InputStream targetStream = new ByteArrayInputStream(pdfDoc.toByteArray());
IOUtils.copy(targetStream, output);
output.closeEntry();
}
output.finish();
output.close();
return (StreamedContent) output;
}
catch(Exception ex)
{
System.out.println("error when generating...");
ex.printStackTrace();
}
}
}
return null;
}
You cannot simply cast a ZipOutputStream to StreamedContent as they don't have a parent child relation. See How can I cast objects that don't inherit each other?.
You should convert your InputStream (not the output stream) to streamed content. See for example https://www.primefaces.org/showcase/ui/file/download.xhtml
So, you need to do something like:
DefaultStreamedContent.builder()
.name("PO_Reports.zip")
.contentType("application/zip")
.stream(() -> yourInputStream)
.build();
I found solutions to these problems. Maybe this solution will help someone else. I would be grateful for any contribution on the new solution.
public StreamedContent printsAction() {
ByteArrayInputStream bis = null;
InputStream stream = null;
if (!termsAgreed) {
RequestContext.getCurrentInstance().execute("PF('warningDialog').show();");
} else {
if (getSelectedPurchaseOrders() != null && !getSelectedPurchaseOrders().isEmpty()) {
try {
if (zipBytes() != null) {
bis = new ByteArrayInputStream(zipBytes()); // Firstly I zip every PDF doc with zipBytes() method
stream = bis;
file = new DefaultStreamedContent(stream, "application/zip", "PO_Reports.zip",StandardCharsets.UTF_8.name());
return file;
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (bis != null) {
bis.close();
}
if (stream != null) {
stream.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
} else {
return null;
}
}
return null;
}
private byte[] zipBytes() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ByteArrayOutputStream pdfDoc = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos);
DataInputStream pdfDocIs = null;
byte[] result = null;
try {
for(PurchaseOrderSAP purchaseOrder : getSelectedPurchaseOrders()) {
pdfDoc = purchaseOrderSAPService.printOrder(purchaseOrder.getPO_NUMBER()); // PDF document comes from SAP as ByteArrayOutputStream
pdfDocIs = new DataInputStream(new ByteArrayInputStream(pdfDoc.toByteArray()));
ZipEntry zipEntry = new ZipEntry("PO_Report_" + purchaseOrder.getPO_NUMBER() + ".pdf");
zos.putNextEntry(zipEntry);
zos.write(toByteArray(pdfDocIs)); // Secondly in order to zip PDF doc i convert it to Byte Array with toByteArray method
}
zos.close();
result = baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
finally {
try {
if (baos != null) {
baos.close();
}
if (pdfDoc != null) {
pdfDoc.close();
}
if (zos != null) {
zos.close();
}
if (pdfDocIs != null) {
pdfDocIs.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
return result;
}
public static byte[] toByteArray(InputStream in) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
byte[] result = null;
int len;
// read bytes from the input stream and store them in buffer
try {
while ((len = in.read(buffer)) != -1) {
// write bytes from the buffer into output stream
os.write(buffer, 0, len);
}
result = os.toByteArray();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (os != null) {
os.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
return result;
}

How can I gzip an InputStream and return an InputStream?

I am starting with a response from a HTTP request:
InputStream responseInputStream = response.getEntityInputStream()
I need to gzip that response so I can upload it to s3 and save it compressed:
this.s3.putObject(new PutObjectRequest(bucketName, key, gzippedResponseInputStream, meta));
I am aware that I can get the byte[] array out of responseInputStream and then gzip them into a new InputStream. However, that can be very inefficient with a large amount of data.
I know that there have been similar questions asked on SO, but I have not found anything that seems to address the specific need of starting with an InputStream and finishing with a gzipped InputStream.
Thanks for any help!
I think you're looking for a PipedInputStream
Here's how it can be done.
public InputStrema getGZipStream() {
final PipedOutputStream pos = new PipedOutputStream();
PipedInputStream pis = new PipedInputStream();
try (final InputStream responseInputStream = response.getEntityInputStream();
){
pis.connect(pos);
Thread thread = new Thread() {
public void run () {
startWriting(pos, responseInputStream);
}
};
thread.start();
} catch(Exception e) {
e.printStackTrace();
}
return pis;
}
public void startWriting(OutputStream out, InputStream in) {
try (GZIPOutputStream gOut = GZIPOutputStream(out);) {
byte[] buffer = new byte[10240];
int len = -1;
while ((len = in.read(buffer)) != -1) {
gOut.write(buffer, 0, len);
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
out.close();
} catch( Exception e) {
e.printStackTrace();
}
}
}
I haven't tested this code, please let me know if this works.
public final class Example {
public static void main(String[] args) throws IOException, InterruptedException {
final PipedInputStream inputStream = new PipedInputStream();
final PipedOutputStream outputStream = new PipedOutputStream(inputStream);
Thread compressorThread = new Thread() {
#Override
public void run() {
try (FileInputStream dataSource = new FileInputStream(args[0])) {
try (GZIPOutputStream sink = new GZIPOutputStream(outputStream)) {
final byte[] buffer = new byte[8 * 1024];
for (int bytesRead = dataSource.read(buffer); bytesRead >= 0; bytesRead = dataSource.read(buffer)) {
sink.write(buffer, 0, bytesRead);
}
}
} catch (IOException ex) {
//TODO handle exception -> maybe use callable + executor
}
}
};
compressorThread.start();
try (FileOutputStream destination = new FileOutputStream(args[1])) {
final byte[] buffer = new byte[8 * 1024];
for (int bytesRead = inputStream.read(buffer); bytesRead >= 0; bytesRead = inputStream.read(buffer)) {
destination.write(buffer, 0, bytesRead);
}
}
compressorThread.join();
}
}
You are right, my previous example was wrong. You can use piped streams. The catch here is that you cannot use the input and output stream from the same thread. Also don't forget to join() on the writing thread. You can test my example by supplyng two parameters:
args[0] -> the source file
args[1] -> the destination to write the compressed content
PS: #11thdimension was a few minutes faster with his piped stream solutions, so if you find this helpful please accept his answer

Runtime error :java.rmi.NoSuchObjectException: no such object in table

i'm attempting to fetch a list of files from server and copy them to directory .
and this error prompts.
java.rmi.NoSuchObjectException: no such object in table
at javafxhomeui_1.HomeUI_2Controller.writeFileToLocalHDD(HomeUI_2Controller.java:427)
at javafxhomeui_1.HomeUI_2Controller.initialize(HomeUI_2Controller.java:312)
HomeUI_2Controller.java
RemoteInputStream ris= null;
File[] iconlist=null;
try {
File appicon=new File("D:\\SERVER\\Server Content\\Apps\\icons");
iconlist=appicon.listFiles();
for (File file1 : iconlist) {
ris = downloadcontroller.getFile(file1.getAbsolutePath());
System.out.println(file1.getName());
}
} catch (Exception ex) {
Logger.getLogger(HomeUI_2Controller.class.getName()).log(Level.SEVERE, null, ex);
}
try {
for (File file1 : iconlist) {
//System.out.println("D:\\client\\Temp\\"+file1.getName());
/*line:312 */ writeFileToLocalHDD(ris,"D:\\client\\Temp\\"+file1.getName());
}
} catch (Exception ex) {
Logger.getLogger(HomeUI_2Controller.class.getName()).log(Level.SEVERE, null, ex);
}
public static void writeFileToLocalHDD(RemoteInputStream inFile, String fileLocation) throws IOException {
// wrap RemoteInputStream as InputStream (all compression issues are dealt
// with in the wrapper code)
/* line:427*/ InputStream istream = RemoteInputStreamClient.wrap(inFile);
BufferedInputStream bis = new BufferedInputStream(istream);
//downloaded file...
File file = new File(fileLocation);
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream fileOutputStream = new FileOutputStream(file);
FileChannel channel = fileOutputStream.getChannel();
byte b[] = new byte[1024];
long startTime = System.currentTimeMillis();
while (bis.available()>0) {
bis.read(b);
System.out.println((System.currentTimeMillis() - startTime)/1000);
ByteBuffer buffer = ByteBuffer.wrap(b);
channel.write(buffer);
}
bis.close();
fileOutputStream.flush();
channel.close();
fileOutputStream.close();
}
////////////////////////////////////////////////
public RemoteInputStream getFile(String fileName) throws IOException {
// create a RemoteStreamServer (note the finally block which only releases
// the RMI resources if the method fails before returning.)
//read data
RemoteInputStreamServer istream = null;
try {
File file = new File(fileName);
System.out.println(file.exists());
FileInputStream fileInputStream = new FileInputStream(file);
BufferedInputStream bufferedInputStream = new BufferedInputStream(
fileInputStream);
istream = new SimpleRemoteInputStream(bufferedInputStream);
// export the final stream for returning to the client
//send data
RemoteInputStream result = istream.export();
// after all the hard work, discard the local reference (we are passing
// responsibility to the client)
istream = null;
return result;
} finally {
// we will only close the stream here if the server fails before
// returning an exported stream
if (istream != null) {
istream.close();
}
}
}
////////////////////////////////////
rmi works on a stub and skeleton structure.
Exception that is thrown has something to do with rmi Registry. The Object is not found because it is not available from RMI resistry.
A NoSuchObjectException is thrown if an attempt is made to invoke a method on an object that no longer exists in the remote virtual machine
as in javaDoc of the Exception thrown

Getting damaged file error after downloading a pdf

I followed this example to let the user download the pdf :
Vaadin Example
/** Copied from example */
#Override
protected void init(VaadinRequest request) {
Button downloadButton = new Button("Download image");
StreamResource myResource = createResource();
FileDownloader fileDownloader = new FileDownloader(myResource);
fileDownloader.extend(downloadButton);
setContent(downloadButton);
}
/** modified from example */
private StreamResource createResource() throws IOException {
return new StreamResource(new StreamSource() {
byte[] bytes =loadFile("/home/amira/Desktop/repTest.pdf"); //Get the file bytes here
InputStream is = new ByteArrayInputStream(bytes);
#Override
public InputStream getStream() {
return is ;
}
}, "report.pdf");
}
public static byte[] readFully(InputStream stream) throws IOException {
byte[] buffer = new byte[8192];
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int bytesRead;
while ((bytesRead = stream.read(buffer)) != -1) {
baos.write(buffer, 0, bytesRead);
}
return baos.toByteArray();
}
public static byte[] loadFile(String sourcePath) throws IOException {
InputStream inputStream = null;
try {
inputStream = new FileInputStream(sourcePath);
return readFully(inputStream);
} finally {
if (inputStream != null) {
inputStream.close();
}
}
}
When I try to open the file , it says damaged file
See my working sample (tested for 20Mb PDFs):
private void downloadDocument()
{
final String retrievalName = "222.pdf";
class FileDownloadResource extends FileResource
{
public FileDownloadResource(File sourceFile, Application application)
{
super(sourceFile, application);
}
public DownloadStream getStream()
{
try
{
byte[] DocContent = null;
DocContent = getFileBytes("C:\\Temp\\222.pdf");
if (DocContent != null)
{
final DownloadStream ds = new DownloadStream(new ByteArrayInputStream(DocContent), "application/pdf", retrievalName);
ds.setCacheTime(getCacheTime());
String fileName = URLEncoder.encode(retrievalName, "UTF8");
// force download!
ds.setParameter("Content-Disposition", "attachment; filename*=\"utf-8'" + fileName + "\"");
return ds;
}
else
{
return null;
}
}
catch (Exception e1)
{
e1.printStackTrace();
return null;
}
}
}
getApplication().getMainWindow().open(new FileDownloadResource(new File(retrievalName), getApplication()));
}
/**
* getFileBytes
*
* #author NBochkarev
*
* #param fileOut
* #return
* #throws IOException
*/
public static byte[] getFileBytes(String fileName) throws IOException
{
ByteArrayOutputStream ous = null;
InputStream ios = null;
try
{
byte[] buffer = new byte[4096];
ous = new ByteArrayOutputStream();
ios = new FileInputStream(new File(fileName));
int read = 0;
while ((read = ios.read(buffer)) != -1)
ous.write(buffer, 0, read);
}
finally
{
try
{
if (ous != null)
ous.close();
}
catch (IOException e)
{
// swallow, since not that important
}
try
{
if (ios != null)
ios.close();
}
catch (IOException e)
{
// swallow, since not that important
}
}
return ous.toByteArray();
}

Categories