/**
*
*/
package de.tutorials;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import org.eclipse.jface.text.link.LinkedModeUI.ExitFlags;
/**
* @author Tom
*
*/
public class ImageIndexExample {
/**
* @param args
*/
public static void main(String[] args) throws Exception {
File imageDir = new File("C:/TEMP/bubu");
File imageIndex = new File("C:/TEMP/bubu/images.index");
File imageThumbs = new File("C:/TEMP/bubu/images.thumb");
ImageThumbnailIndex imageThumbnailIndex = new ImageThumbnailIndex(
imageIndex, imageThumbs);
imageThumbnailIndex.open();
imageThumbnailIndex.rebuildIndex(imageDir);
displayThumbs(imageThumbnailIndex);
}
private static void displayThumbs(
final ImageThumbnailIndex imageThumbnailIndex) throws Exception {
JFrame frm = new JFrame("ImageIndexExample");
frm.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
try {
imageThumbnailIndex.close(true);
} catch (IOException e1) {
e1.printStackTrace();
}
System.exit(0);
}
});
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(imageThumbnailIndex
.getValidRecordCount() / 4, 4));
JScrollPane scrollPane = new JScrollPane(panel);
scrollPane.setPreferredSize(new Dimension(400, 300));
initComponents(panel, imageThumbnailIndex);
frm.add(scrollPane);
frm.pack();
frm.setVisible(true);
}
private static void initComponents(final JPanel panel,
final ImageThumbnailIndex imageThumbnailIndex) throws Exception {
for (final ImageIndexRecord record : imageThumbnailIndex.getRecordSet()) {
if (record.isValid()) {
final JButton button = new JButton(new ImageIcon(
imageThumbnailIndex.getThumbnailFor(record)));
button.setToolTipText(record.getImagePath());
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// try {
// imageThumbnailIndex.delete(record);
// panel.remove(button);
// panel.updateUI();
// } catch (Exception e1) {
// e1.printStackTrace();
// }
JFrame imageDisplay = new JFrame(record.getImagePath());
imageDisplay.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
imageDisplay.add(new JLabel(new ImageIcon(record.getImagePath())));
imageDisplay.pack();
imageDisplay.setVisible(true);
}
});
panel.add(button);
}
}
}
static class ImageThumbnailIndex implements Closeable {
Set<ImageIndexRecord> records;
File indexFile;
File thumbnailFile;
int lastIndexOffset;
int lastThumbnailOffset;
RandomAccessFile indexRandomAccessFile;
FileOutputStream thumbnailOutputStream;
RandomAccessFile thumbnailRandomAccess;
int validRecordCount;
BufferedImage scaledImage;
FilenameFilter imageFileNameFilter = new FilenameFilter() {
public boolean accept(File dir, String name) {
String fileName = name.toLowerCase();
return fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")
|| fileName.endsWith(".gif")
|| fileName.endsWith(".png");
}
};
public ImageThumbnailIndex(File indexFile, File thumbnailFile) {
this.indexFile = indexFile;
this.thumbnailFile = thumbnailFile;
}
void open() throws Exception {
if (!indexFile.exists()) {
indexFile.createNewFile();
}
DataInputStream indexDataInputStream = new DataInputStream(
new FileInputStream(indexFile));
while (indexDataInputStream.available() > 0) {
int currentThumbnailOffset = indexDataInputStream.readInt();
int currentIndexOffset = indexDataInputStream.readInt();
int code = indexDataInputStream.readInt();
int chars = indexDataInputStream.readInt();
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < chars; i++) {
stringBuilder.append(indexDataInputStream.readChar());
}
int imageSize = indexDataInputStream.readInt();
int originalImageSize = indexDataInputStream.readInt();
int originalFileTimeStamp = indexDataInputStream.readInt();
ImageIndexRecord imageIndexRecord = new ImageIndexRecord(
currentThumbnailOffset, currentIndexOffset, code,
stringBuilder.toString(), imageSize, originalImageSize,
originalFileTimeStamp);
getRecords().add(imageIndexRecord);
if (imageIndexRecord.isValid()) {
validRecordCount++;
}
lastThumbnailOffset += imageSize;
}
lastIndexOffset = (int) indexFile.length();
indexDataInputStream.close();
}
public BufferedImage getThumbnailFor(ImageIndexRecord imageIndexRecord)
throws Exception {
if (null == thumbnailRandomAccess) {
thumbnailRandomAccess = new RandomAccessFile(thumbnailFile,
"rw");
}
long oldPosition = thumbnailRandomAccess.getFilePointer();
thumbnailRandomAccess.seek(imageIndexRecord.getOffset());
byte[] imageData = new byte[imageIndexRecord.getImageSize()];
thumbnailRandomAccess.read(imageData, 0, imageIndexRecord
.getImageSize());
BufferedImage thumbnail = ImageIO.read(new ByteArrayInputStream(
imageData));
thumbnailRandomAccess.seek(oldPosition);
return thumbnail;
}
public void rebuildIndex(File imageDirectory) throws Exception {
long time = -System.currentTimeMillis();
for (File imageFile : imageDirectory.listFiles(imageFileNameFilter)) {
if (!contains(imageFile)) {
index(imageFile);
} else {
ImageIndexRecord imageIndexRecord = getValidImageIndexRecordBy(imageFile);
if (null == imageIndexRecord) {
ImageIndexRecord deletedImageIndexRecord = getImageIndexRecordBy(imageFile);
if (imageFileChanged(imageFile, deletedImageIndexRecord)) {
index(imageFile);
}
} else if (imageFileChanged(imageFile, imageIndexRecord)) {
delete(imageIndexRecord);
index(imageFile);
}
}
}
time += System.currentTimeMillis();
System.out.println("index rebuild took: " + (time / 1000) + "s");
}
/**
* @param imageFile
* @param imageIndexRecord
* @return
*/
private boolean imageFileChanged(File imageFile,
ImageIndexRecord imageIndexRecord) {
return !sameFileSize(imageFile, imageIndexRecord)
&& !sameLastModified(imageFile, imageIndexRecord);
}
private void index(File imageFile) throws Exception {
System.out.println("indexing: " + imageFile.getAbsolutePath());
if (null == indexRandomAccessFile) {
indexRandomAccessFile = new RandomAccessFile(indexFile, "rw");
}
if (null == thumbnailOutputStream) {
thumbnailOutputStream = new FileOutputStream(thumbnailFile,
true);
}
indexRandomAccessFile.seek(lastIndexOffset);
long indexOffset = indexRandomAccessFile.getFilePointer();
indexRandomAccessFile.writeInt(lastThumbnailOffset); // currentThumbnailOffset
indexRandomAccessFile.writeInt((int) indexOffset);
indexRandomAccessFile.writeInt(ImageIndexRecord.VALID); // VALID
String imageFilePath = imageFile.getAbsolutePath();
indexRandomAccessFile.writeInt(imageFilePath.length()); // char-count
indexRandomAccessFile.writeChars(imageFilePath); // chars...
BufferedImage image = ImageIO.read(imageFile);
getScaledImage().getGraphics()
.drawImage(image, 0, 0, 120, 90, null);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(scaledImage, "jpeg", byteArrayOutputStream);
int originalFileTimeStamp = (int) imageFile.lastModified();
ImageIndexRecord imageIndexRecord = new ImageIndexRecord(
lastThumbnailOffset, (int) indexOffset,
ImageIndexRecord.VALID, imageFilePath,
byteArrayOutputStream.size(), (int) imageFile.length(),
originalFileTimeStamp);
getRecords().add(imageIndexRecord);
lastThumbnailOffset += byteArrayOutputStream.size();
thumbnailOutputStream.write(byteArrayOutputStream.toByteArray()); // imagedata
thumbnailOutputStream.flush();
indexRandomAccessFile.writeInt(byteArrayOutputStream.size()); // thumbnail
// size
indexRandomAccessFile.writeInt((int) imageFile.length()); // original
// size
indexRandomAccessFile.writeInt(originalFileTimeStamp);
lastIndexOffset += (indexRandomAccessFile.getFilePointer() - lastIndexOffset);
}
private void delete(ImageIndexRecord imageIndexRecord) throws Exception {
if (null == indexRandomAccessFile) {
indexRandomAccessFile = new RandomAccessFile(indexFile, "rw");
}
imageIndexRecord.code = ImageIndexRecord.DELETED;
validRecordCount--;
int indexOffset = imageIndexRecord.getIndexOffset();
indexRandomAccessFile.seek(indexOffset);
indexRandomAccessFile.skipBytes(Integer.SIZE / Byte.SIZE);
indexRandomAccessFile.skipBytes(Integer.SIZE / Byte.SIZE);
indexRandomAccessFile.writeInt(ImageIndexRecord.DELETED);
}
private ImageIndexRecord getImageIndexRecordBy(File imageFile) {
for (ImageIndexRecord imageIndexRecord : getRecords()) {
if (sameFileName(imageFile, imageIndexRecord)
&& sameFileSize(imageFile, imageIndexRecord)
&& sameLastModified(imageFile, imageIndexRecord)) {
return imageIndexRecord;
}
}
return null;
}
/**
* @param imageFile
* @param imageIndexRecord
* @return
*/
private boolean sameLastModified(File imageFile,
ImageIndexRecord imageIndexRecord) {
return imageIndexRecord.getOriginalFileTimeStamp() == (int) imageFile
.lastModified();
}
/**
* @param imageFile
* @param imageIndexRecord
* @return
*/
private boolean sameFileSize(File imageFile,
ImageIndexRecord imageIndexRecord) {
return imageIndexRecord.getOriginalSize() == (int) imageFile
.length();
}
/**
* @param imageFile
* @param imageIndexRecord
* @return
*/
private boolean sameFileName(File imageFile,
ImageIndexRecord imageIndexRecord) {
return imageIndexRecord.getImagePath().equals(imageFile.getAbsolutePath());
}
private ImageIndexRecord getValidImageIndexRecordBy(File imageFile) {
ImageIndexRecord imageIndexRecord = getImageIndexRecordBy(imageFile);
if (null != imageIndexRecord && imageIndexRecord.isValid()) {
return imageIndexRecord;
}
return null;
}
public boolean contains(File imageFile) {
for (ImageIndexRecord imageIndexRecord : getRecords()) {
if (sameFileName(imageFile, imageIndexRecord)) {
return true;
}
}
return false;
}
private Set<ImageIndexRecord> getRecords() {
if (null == records) {
records = new HashSet<ImageIndexRecord>(128);
}
return records;
}
public Set<ImageIndexRecord> getRecordSet() {
return Collections.unmodifiableSet(getRecords());
}
public void close(boolean compact) throws IOException {
if (compact) {
compact();
}
close();
}
private void compact() {
// TODO
// REMOVE deleted index entries and according thumbnails
// ADJUST indexOffsets and according thumbnailOffsets
}
/**
* @return the scaledImage
*/
public BufferedImage getScaledImage() {
if (null == scaledImage) {
scaledImage = new BufferedImage(120, 90,
BufferedImage.TYPE_INT_RGB);
}
return scaledImage;
}
public void close() throws IOException {
System.out.println("close");
if (null != this.indexRandomAccessFile)
this.indexRandomAccessFile.close();
if (null != this.thumbnailOutputStream)
this.thumbnailOutputStream.close();
if (null != this.thumbnailRandomAccess)
this.thumbnailRandomAccess.close();
this.records.clear();
this.records = null;
this.scaledImage = null;
}
/**
* @return the validRecordCount
*/
public int getValidRecordCount() {
return validRecordCount;
}
}
static class ImageIndexRecord {
final static int VALID = 1;
final static int DELETED = 2;
int offset;
int indexOffset;
int code;
String imagePath;
int imageSize;
int originalSize;
int originalFileTimeStamp;
/**
* @param offset
* @param imageNameLength
* @param imagePath
* @param imageSize
*/
public ImageIndexRecord(int offset, int indexOffset, int code,
String imagePath, int imageSize, int originalSize,
int originalFileTimeStamp) {
super();
this.offset = offset;
this.indexOffset = indexOffset;
this.code = code;
this.imagePath = imagePath;
this.imageSize = imageSize;
this.originalSize = originalSize;
this.originalFileTimeStamp = originalFileTimeStamp;
}
/**
* @return the code
*/
public int getCode() {
return code;
}
/**
* @return the offset
*/
public int getOffset() {
return offset;
}
/**
* @return the imageName
*/
public String getImagePath() {
return imagePath;
}
/**
* @return the imageSize
*/
public int getImageSize() {
return imageSize;
}
/**
* @return the originalSize
*/
public int getOriginalSize() {
return originalSize;
}
public boolean isDeleted() {
return getCode() == DELETED;
}
public boolean isValid() {
return getCode() == VALID;
}
/**
* @return the indexOffset
*/
public int getIndexOffset() {
return indexOffset;
}
/**
* @return the originalFileTimeStamp
*/
public int getOriginalFileTimeStamp() {
return originalFileTimeStamp;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((imagePath == null) ? 0 : imagePath.hashCode());
result = prime * result + originalFileTimeStamp;
result = prime * result + originalSize;
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final ImageIndexRecord other = (ImageIndexRecord) obj;
if (imagePath == null) {
if (other.imagePath != null)
return false;
} else if (!imagePath.equals(other.imagePath))
return false;
if (originalFileTimeStamp != other.originalFileTimeStamp)
return false;
if (originalSize != other.originalSize)
return false;
return true;
}
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("Image=");
stringBuilder.append(imagePath);
stringBuilder.append(" IndexOffset=");
stringBuilder.append(indexOffset);
stringBuilder.append(" ThumbOffset=");
stringBuilder.append(offset);
stringBuilder.append(" code=");
stringBuilder.append(code == VALID ? "VALID" : "DELETED");
stringBuilder.append(" ThumbSize=");
stringBuilder.append(imageSize);
stringBuilder.append(" OriginalSize=");
stringBuilder.append(originalSize);
stringBuilder.append(" OriginalFileTimeStamp=");
stringBuilder.append(originalFileTimeStamp);
return stringBuilder.toString();
}
}
}