archive file in image

galaxyAbstractor

Community Advocate
Community Support
Messages
5,508
Reaction score
35
Points
48
*sigh*
You do know what break means, right? :p

It's not very nice to use a string for "valid" though. You only need 3 values so it would be a better idea to use a short.

Not really, it cancels the whole loop? I thought it only canceled the current run.

Anyways, it returns false when there is a zip archive in the image
 

ah-blabla

New Member
Messages
375
Reaction score
7
Points
0
It's not very nice to use a string for "valid" though. You only need 3 values so it would be a better idea to use a short.
An enum would probably be better for readability.
 
Last edited:

marshian

New Member
Messages
526
Reaction score
9
Points
0
Not really, it cancels the whole loop? I thought it only canceled the current run.

Anyways, it returns false when there is a zip archive in the image
Continue does that. Break cancels the whole loop.

Code:
for(int i=0; i<4; i++) {
	int c = inFile.read();
	if(c == filesigrar[i]) {
		valid = "RAR";
	} else if(c == filesigzip[i]) {
		valid = "ZIP";
	} else {
		//In case any of the 4 bytes fails the check, the file is invalid
		//and the loop is aborted.
		valid = "false";
		break;
	}
}

An enum would probably be better for readability.
I think it's a *bit* of an overkill to write an enum to do this... You could use 'f', 'z' and 'r' too, that's readable enough imho :p
 

ah-blabla

New Member
Messages
375
Reaction score
7
Points
0
I think it's a *bit* of an overkill to write an enum to do this... You could use 'f', 'z' and 'r' too, that's readable enough imho :p
I know I'm being pedantic, but: it's better practice (to use enums) in this sort of situation, and prevents you from getting into bad habits. You could use f, z, r, but then you'd have to write comments on it so you remember what it meant when you read the code in half a year (well, you might not, but there is the chance), whereas with an enum it is clear what everything means, e.g:
Code:
public enum Status {NONE, ZIP, RAR}
private Status stat = Status.NONE;

stat = Status.ZIP;
looks a lot better / is easier to understand.
The enum is also a lot more future proof, making it easier to extend to extend the program should you wish to in the future, and it's also better if you modify the class to use as part of a larger program.
 

galaxyAbstractor

Community Advocate
Community Support
Messages
5,508
Reaction score
35
Points
48
Continue does that. Break cancels the whole loop.

Code:
for(int i=0; i<4; i++) {
    int c = inFile.read();
    if(c == filesigrar[i]) {
        valid = "RAR";
    } else if(c == filesigzip[i]) {
        valid = "ZIP";
    } else {
        //In case any of the 4 bytes fails the check, the file is invalid
        //and the loop is aborted.
        valid = "false";
        break;
    }
}
I think it's a *bit* of an overkill to write an enum to do this... You could use 'f', 'z' and 'r' too, that's readable enough imho :p

Thanks a lot, marshian :D
 

galaxyAbstractor

Community Advocate
Community Support
Messages
5,508
Reaction score
35
Points
48
oh one more thing, is it possible to make the rows background color green if an archive is found?
 

galaxyAbstractor

Community Advocate
Community Support
Messages
5,508
Reaction score
35
Points
48
You're welcome :)

*looks confused*
What rows?

Oh so I had right in my dream that I forgot to say I put the list of files in a jTable xD

Capture-50.png

Those rows where there were an archive found
 

galaxyAbstractor

Community Advocate
Community Support
Messages
5,508
Reaction score
35
Points
48
That's not something that you can easily do, but I found this on the internet.
http://elliotth.blogspot.com/2004/09/alternating-row-colors-in-jtable.html
You can quite easily adapt it to your own needs and use it in your application.

Actually it was quite easy, after I found this:
http://www.java-forums.org/awt-swin...ble-row-having-particular-value.html#post1015 and http://www.javaworld.com/javaworld/javaqa/2001-09/03-qa-0928-jtable.html?page=1

Code:
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package hidefiles;

/**
 *
 * @author vigge_sWe
 */
import java.awt.Component;
import java.awt.Color;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
public class CustomTableCellRenderer extends DefaultTableCellRenderer
{
    @Override
    public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int col)
{
     Component comp = super.getTableCellRendererComponent(
                      table,  value, isSelected, hasFocus, row, col);

     String s =  table.getModel().getValueAt(row, 2).toString();

     if(s.equalsIgnoreCase("RAR") || s.equalsIgnoreCase("ZIP"))
     {
         comp.setBackground(new Color(0x458358));
     }
     else
     {
         comp.setBackground(null);
     }

     return( comp );
 }
}

Code:
model.addColumn("Image");
        model.addColumn("Size");
        model.addColumn("Archive");
        jTable1.setModel(model);
        TableCellRenderer renderer = new CustomTableCellRenderer();

        jTable1.setDefaultRenderer( jTable1.getColumnClass(2), renderer );

Capture-51.png


Now to the last question, I promise ;). How would I return the size of the image in the file we added?
 
Last edited:

marshian

New Member
Messages
526
Reaction score
9
Points
0
The file format is like this:
<image data><archive data><integer><signature>
Both integer and signature are 4 byte long, so to read integer skip to the 8th last byte and read an integer through an ObjectInputStream.
 

galaxyAbstractor

Community Advocate
Community Support
Messages
5,508
Reaction score
35
Points
48
The file format is like this:
<image data><archive data><integer><signature>
Both integer and signature are 4 byte long, so to read integer skip to the 8th last byte and read an integer through an ObjectInputStream.

so the size of the image represented there will always be 4 bytes long?
 

ah-blabla

New Member
Messages
375
Reaction score
7
Points
0
so the size of the image represented there will always be 4 bytes long?
If you store the size as an integer then yes, since an integer is 4 bytes long in Java (or rather, it's 32 bits according to specification (see http://java.sun.com/docs/books/tutorial/java/nutsandbolts/datatypes.html). The only problem is if the file's length is larger than the data that an int can store, since you are storing the image size using the method File.size() which returns the file length in bytes. The largest number an int can store is 2,147,483,647 , so if your image file is longer than 2,147,483,647 bytes (2GB!) then the int won't be able to store the length of the file, but I have never come across images that large.

And be careful when reading the integer in through the ObjectInputStream, since int is a primitive type you must read it in using the method readInt(). I also just read this
Primitive data, excluding serializable fields and externalizable data, is written to the ObjectOutputStream in block-data records. A block data record is composed of a header and data. The block data header consists of a marker and the number of bytes to follow the header. Consecutive primitive data writes are merged into one block-data record. The blocking factor used for a block-data record will be 1024 bytes. Each block-data record will be filled up to 1024 bytes, or be written whenever there is a termination of block-data mode. Calls to the ObjectOutputStream methods writeObject, defaultWriteObject and writeFields initially terminate any existing block-data record.
(from http://java.sun.com/javase/6/docs/api/index.html)
That means that the int won't be 4 bytes long if you write it using ObjectOutputStream. More research is probably needed, but it might be better to use a normal OutputStream to write the integer and use a normal InputStream to read the int again.


EDIT://
I've written a whole program doing this. It's a bit long to post here, so I've posted it here:
http://www.ahunt.org/programming/image-archive.html
The code isn't particularly well commented / formatted etc, but it works, and should give you an idea of what to do. I haven't added in filetype signatures yet, but that would be quite simple really. You can then use the class as used in the example main() method.
 
Last edited:

galaxyAbstractor

Community Advocate
Community Support
Messages
5,508
Reaction score
35
Points
48
So I should use an ObjectInputStream?

Code:
private void jTable1MouseClicked(java.awt.event.MouseEvent evt) {
        if(evt.getClickCount() == 2 &&
                (jTable1.getValueAt(jTable1.getSelectedRow(), 2).toString().equals("RAR") ||
                 jTable1.getValueAt(jTable1.getSelectedRow(), 2).toString().equals("ZIP")) ){
            zipFileLabel.setText(jTable1.getValueAt(jTable1.getSelectedRow(), 0).toString());
            zipType.setText(jTable1.getValueAt(jTable1.getSelectedRow(), 2).toString());
            
            ObjectInputStream inFile = null;
            File input = new File(findDir + jTable1.getValueAt(jTable1.getSelectedRow(), 0).toString());
          
            try {
                inFile = new ObjectInputStream((InputStream) new FileInputStream(input));
            } catch (IOException ex) {
                Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
            }
           
            try {
                //Go to the last 4 bytes
                inFile.skip(input.length() - 8);
                int imageSize = inFile.readInt();
                System.out.println(imageSize);
                inFile.close();
            } catch (IOException ex) {
                Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
            }

            jFrame3.pack();
            jFrame3.setVisible(true);
        }
    }
(findDir is the path to the folder where the file resides in)

Where did it go wrong? It worked before I added the ObjectInputStream

2009-okt-11 15:12:05 hidefiles.Main jTable1MouseClicked
ALLVARLIG: null
java.io.StreamCorruptedException: invalid stream header: 89504E47
at java.io_ObjectInputStream.readStreamHeader(ObjectInputStream.java:783)
at java.io_ObjectInputStream.<init>(ObjectInputStream.java:280)
at hidefiles.Main.jTable1MouseClicked(Main.java:638)
at hidefiles.Main.access$1000(Main.java:39)
at hidefiles.Main$9.mouseClicked(Main.java:397)
at java.awt.AWTEventMulticaster.mouseClicked(AWTEventMulticaster.java:253)
at java.awt.Component.processMouseEvent(Component.java:6219)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3265)
at java.awt.Component.processEvent(Component.java:5981)
at java.awt.Container.processEvent(Container.java:2041)
at java.awt.Component.dispatchEventImpl(Component.java:4583)
at java.awt.Container.dispatchEventImpl(Container.java:2099)
at java.awt.Component.dispatchEvent(Component.java:4413)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4556)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4229)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4150)
at java.awt.Container.dispatchEventImpl(Container.java:2085)
at java.awt.Window.dispatchEventImpl(Window.java:2475)
at java.awt.Component.dispatchEvent(Component.java:4413)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at hidefiles.Main.jTable1MouseClicked(Main.java:645)
at hidefiles.Main.access$1000(Main.java:39)
at hidefiles.Main$9.mouseClicked(Main.java:397)
at java.awt.AWTEventMulticaster.mouseClicked(AWTEventMulticaster.java:253)
at java.awt.Component.processMouseEvent(Component.java:6219)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3265)
at java.awt.Component.processEvent(Component.java:5981)
at java.awt.Container.processEvent(Container.java:2041)
at java.awt.Component.dispatchEventImpl(Component.java:4583)
at java.awt.Container.dispatchEventImpl(Container.java:2099)
at java.awt.Component.dispatchEvent(Component.java:4413)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4556)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4229)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4150)
at java.awt.Container.dispatchEventImpl(Container.java:2085)
at java.awt.Window.dispatchEventImpl(Window.java:2475)
at java.awt.Component.dispatchEvent(Component.java:4413)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

Sorry I'm such a noob xD
 
Last edited:

ah-blabla

New Member
Messages
375
Reaction score
7
Points
0
Sorry for being a bit confusing earlier on, I'll try and explain a bit more simply (however this isn't such a simple thing...)
The problem with ObjectOutputStream is that when you write an int to a file, it doesn't simply write the 4 bytes that the int contains, but it first of all writes a header, so all the positions will be different than expected. If you then try to read this int with an ObjectInputStream it expects a header tellling it what the data is, for this to work you need to know where the int+header actually starts, which will not be 8 bytes from the end.
Instead you should try to write the int using the normal OutputStream.write() method, and then read using InputStream.read().A slight complication is that InputStream / OutputStream read and write in bytes, while an int is 4 bytes. The OutputStream.write(int) method only takes the first 8 bits (first byte) of the int, the rest is ignored, so what you need to do is convert the int to a byte array of length four before writing, since if you pass a byte array to OutputStream.write(byte[]) it prints out all 4 bytes. Similarly when reading you need to create an empty byte array of the length 4, skip to 8 bytes from the end of the file, then use InputStream.read(byte[]) with that byte array, and then read the byte array again.

I.e. when writing to file, write the length using:
Code:
// These two things are probably already setup, so you can remove them.
int length = WhateverItEquals;
OutputStream os = whateverYouSetUpEarlier;
// This here converts length to a byte array and writes it.
os.write(new byte[] {
                (byte)(length >>> 24),
                (byte)(length >>> 16),
                (byte)(length >>> 8),
                (byte)length});
To read this you then use the following code:
Code:
InputStream is = WhateverYouSetUp;
// The byte array into which the length will be read.
byte[] b = new byte[4];
// Skip to where the int is
is.skip(File.length() - 8);
// This then reads in the next four bytes into the array.
is.read(lengthArray);
// This converts the byte array back to an int and stores it.
int length = (b[0] << 24)
                ((b[1] & 0xFF) << 16)
                ((b[2] & 0xFF) << 8)
                (b[3] & 0xFF);

And a slight bit of advice: a good idea would be to create a separate class which loads a file, does all the analysis at once, and from which you can then simply request information about the file, since it seems you are reading the file again and again for each piece of information you want from it.

Another suggestion is to add another part to the file storing the name of the file you have archive, i.e. the new format of the file would be:
<image><file_name><file><image_length><file_name_length>
That would allow you to easily store any file within the image, since only rar and zip is slightly limiting.
 
Last edited:

galaxyAbstractor

Community Advocate
Community Support
Messages
5,508
Reaction score
35
Points
48
Sorry for being a bit confusing earlier on, I'll try and explain a bit more simply (however this isn't such a simple thing...)
The problem with ObjectOutputStream is that when you write an int to a file, it doesn't simply write the 4 bytes that the int contains, but it first of all writes a header, so all the positions will be different than expected. If you then try to read this int with an ObjectInputStream it expects a header tellling it what the data is, for this to work you need to know where the int+header actually starts, which will not be 8 bytes from the end.
Instead you should try to write the int using the normal OutputStream.write() method, and then read using InputStream.read().A slight complication is that InputStream / OutputStream read and write in bytes, while an int is 4 bytes. The OutputStream.write(int) method only takes the first 8 bits (first byte) of the int, the rest is ignored, so what you need to do is convert the int to a byte array of length four before writing, since if you pass a byte array to OutputStream.write(byte[]) it prints out all 4 bytes. Similarly when reading you need to create an empty byte array of the length 4, skip to 8 bytes from the end of the file, then use InputStream.read(byte[]) with that byte array, and then read the byte array again.

I.e. when writing to file, write the length using:
Code:
// These two things are probably already setup, so you can remove them.
int length = WhateverItEquals;
OutputStream os = whateverYouSetUpEarlier;
// This here converts length to a byte array and writes it.
os.write(new byte[] {
                (byte)(length >>> 24),
                (byte)(length >>> 16),
                (byte)(length >>> 8),
                (byte)length});
To read this you then use the following code:
Code:
InputStream is = WhateverYouSetUp;
// The byte array into which the length will be read.
byte[] b = new byte[4];
// Skip to where the int is
is.skip(File.length() - 8);
// This then reads in the next four bytes into the array.
is.read(lengthArray);
// This converts the byte array back to an int and stores it.
int length = (b[0] << 24)
                ((b[1] & 0xFF) << 16)
                ((b[2] & 0xFF) << 8)
                (b[3] & 0xFF);
And a slight bit of advice: a good idea would be to create a separate class which loads a file, does all the analysis at once, and from which you can then simply request information about the file, since it seems you are reading the file again and again for each piece of information you want from it.

Another suggestion is to add another part to the file storing the name of the file you have archive, i.e. the new format of the file would be:
<image><file_name><file><image_length><file_name_length>
That would allow you to easily store any file within the image, since only rar and zip is slightly limiting.

Well... It works but it puts the wrong length. 358148 bytes on a file that is around 17776 bytes
 

ah-blabla

New Member
Messages
375
Reaction score
7
Points
0
Hmm... Are you sure the length you are using at beginning (ie. when you are making the combined file) is correct? (The same code worked fine for the test program I wrote.) Try posting the full new code, so I can see what exactly is happening.
 

ah-blabla

New Member
Messages
375
Reaction score
7
Points
0
Replace the ObjectOutputStream used in the first file, i.e. replace:
Code:
                out.flush();
                out.close();
                outOOS = new ObjectOutputStream((OutputStream) new FileOutputStream(output, true));
               
               
 if(archiveType.equals("rar")){
                    outwrite(0x58); // Identify archive rar
                    out.write(0x59); // Identify archive rar
                    out.write(0x12); // Identify archive rar
                    out.write(0xA2); // Identify archive rar
                } else if(archiveType.equals("zip")) {
                    out.write(0x58); // Identify archive zip
                    out.write(0x59); // Identify archive zip
                    out.write(0x12); // Identify archive zip
                    out.write(0xA3); // Identify archive zip
                }
                outOOS.flush();
with
Code:
                if(archiveType.equals("rar")){
                    // Identify rar
                    out.write({0x58,0x59,0x12,(byte)0xA2)});
                } else if(archiveType.equals("zip")) {
                    // Identify zip
                    out.write({0x58,0x59,0x12,(byte)0xA3)})
                }
out.close();
(Untested, but should work.)
The code you have there using the ObjectOutputStream (OOS) writes a header for each part of the signature, so the signature is now larger than 4 bytes, which will have been causing the problems. Generally try to avoid using OOS unless you are doing stuff with object serialisation, since that's what it's primarily designed for.
The code opening the file should now work correctly, since both size and signature should be in the expected places.

Also, you seem to be wanting to write a 4 byte signature, however 0xA2 and 0xA3 are integer codes: in the code above it is cast to a byte (i.e. cuts out 3/4 of the data), so you should probably replace that with a code representing a byte. Luckily 0xA2 and 0xA3 when truncated to a byte each represent a different number, but it's still better to use bytes.

And BTW, it isn't necessary to call out.flush() before out.close(), since out.close() will call flush.

Lets hope this fixes it. :cool:
 
Last edited:

galaxyAbstractor

Community Advocate
Community Support
Messages
5,508
Reaction score
35
Points
48
Replace the ObjectOutputStream used in the first file, i.e. replace:
Code:
                out.flush();
                out.close();
                outOOS = new ObjectOutputStream((OutputStream) new FileOutputStream(output, true));
               
               
 if(archiveType.equals("rar")){
                    outwrite(0x58); // Identify archive rar
                    out.write(0x59); // Identify archive rar
                    out.write(0x12); // Identify archive rar
                    out.write(0xA2); // Identify archive rar
                } else if(archiveType.equals("zip")) {
                    out.write(0x58); // Identify archive zip
                    out.write(0x59); // Identify archive zip
                    out.write(0x12); // Identify archive zip
                    out.write(0xA3); // Identify archive zip
                }
                outOOS.flush();
with
Code:
                if(archiveType.equals("rar")){
                    // Identify rar
                    out.write({0x58,0x59,0x12,(byte)0xA2)});
                } else if(archiveType.equals("zip")) {
                    // Identify zip
                    out.write({0x58,0x59,0x12,(byte)0xA3)})
                }
out.close();
(Untested, but should work.)
The code you have there using the ObjectOutputStream (OOS) writes a header for each part of the signature, so the signature is now larger than 4 bytes, which will have been causing the problems. Generally try to avoid using OOS unless you are doing stuff with object serialisation, since that's what it's primarily designed for.
The code opening the file should now work correctly, since both size and signature should be in the expected places.

And BTW, it isn't necessary to call out.flush() before out.close(), since out.close() will call flush.

Lets hope this fixes it. :cool:

"illegal start of expression" and "; expected" when trying that.

Edit: You do mean
Code:
if(archiveType.equals("rar")){
                    // Identify rar
                    out.write(new byte[] {(byte)0x58,(byte)0x59,(byte)0x12,(byte)0xA2});
                } else if(archiveType.equals("zip")) {
                    // Identify zip
                    out.write(new byte[] {(byte)0x58,(byte)0x59,(byte)0x12,(byte)0xA3});
                }
right? Because now it works xD
 
Last edited:
Top