/** * */ package photoSpreadUtilities; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import photoSpread.PhotoSpreadException; import photoSpread.PhotoSpreadException.FileIOException; /** * Creates a quite likely unique thumbprint of * a file. * @author paepcke * */ public class Thumbprint { private static final int firstSamplePlace = 3500; private static final int secondSamplePlace = 7000; private static final int sampleSize = 400; private static final int useAllContentThreshold = secondSamplePlace + sampleSize + 1; private long fileSize = 0; private long firstSampleSum = 0; private long secondSampleSum = 0; /**************************************************** * Factories * @throws FileIOException * @throws photoSpread.PhotoSpreadException.FileNotFoundException *****************************************************/ // FileInputStream --> FileChannel --> ByteBuffer --> loop get(int) // BufferedImage.getRaster() --> Raster.getDataBuffer() --> DataBuffer public Thumbprint (String fileName) throws photoSpread.PhotoSpreadException.FileNotFoundException, FileIOException { computeThumbprint(fileName); } public Thumbprint (File file) throws photoSpread.PhotoSpreadException.FileNotFoundException, FileIOException { computeThumbprint(file); } /**************************************************** * Methods *****************************************************/ public boolean equals (Thumbprint otherThumbprint) { return ((fileSize == otherThumbprint.getFileSize()) && (firstSampleSum == otherThumbprint.getFirstSampleSum()) && (secondSampleSum == otherThumbprint.getSecondSampleSum())); } protected long getFileSize() { return fileSize; } protected long getFirstSampleSum () { return firstSampleSum; } protected long getSecondSampleSum () { return secondSampleSum; } /* * Heavy lifting for thumbprint computation. * @throws FileIOException * @throws photoSpread.PhotoSpreadException.FileNotFoundException * @throws FileIOException */ private void computeThumbprint (String fileName) throws photoSpread.PhotoSpreadException.FileNotFoundException, FileIOException { computeThumbprint(new File(fileName)); } private void computeThumbprint (File file) throws photoSpread.PhotoSpreadException.FileNotFoundException, FileIOException { FileInputStream finStream = null; try { finStream = new FileInputStream(file); FileChannel fChannel = finStream.getChannel(); fileSize = fChannel.size(); if (fChannel.size() <= useAllContentThreshold) { addAllBytes(fChannel, file); return; } // Get a buffer exactly sampleSize bytes large: ByteBuffer byteBuffer = ByteBuffer.allocate(sampleSize); // File pointer to where first sample is to be taken: fChannel.position(firstSamplePlace); // Read all sampleSize bytes: int bytesRead = fChannel.read(byteBuffer); if (bytesRead != sampleSize) { throw new IOException("Cannot read first sample."); } // Add them up to get the sum of the first sample: for (int i=0; i<sampleSize; i++) firstSampleSum += byteBuffer.get(i); // Get a fresh sample buffer: byteBuffer = ByteBuffer.allocate(sampleSize); // File pointer to where second sample is to be taken: fChannel.position(secondSamplePlace); // Read all sampleSize bytes: bytesRead = fChannel.read(byteBuffer); if (bytesRead != sampleSize) { throw new IOException("Cannot read second sample."); } // Add them up to get the sum of the first sample: for (int i=0; i<sampleSize; i++) secondSampleSum += byteBuffer.get(i); } catch (FileNotFoundException e) { throw new PhotoSpreadException.FileNotFoundException("File '" + file.getAbsolutePath() + "' not found."); } catch (IOException e1) { throw new PhotoSpreadException.FileIOException( "File read problem with '" + file.getAbsolutePath() + ".' " + e1.getMessage()); } finally { if (finStream != null) try { finStream.close(); } catch (IOException e1) { throw new PhotoSpreadException.FileIOException( "Cannot close file '" + file.getAbsolutePath() + ".' " + e1.getMessage()); } } } private void addAllBytes(FileChannel fChannel, File file) throws FileIOException { try { // Get a buffer exactly the size of the file bytes large: ByteBuffer byteBuffer = ByteBuffer.allocate((int) fileSize); // Read the entire file, (which is known to be small; // otherwise we would have sampled above): int bytesRead = fChannel.read(byteBuffer); if (bytesRead != sampleSize) { throw new IOException("Cannot read first sample."); } // Add them up to get the sum of the first sample: for (int i=0; i<fileSize; i++) firstSampleSum += byteBuffer.get(i); } catch (IOException e1) { throw new PhotoSpreadException.FileIOException( "File read problem with '" + file.getAbsolutePath() + ".' " + e1.getMessage()); } } /**************************************************** * Test Methods --- Main * @throws FileIOException * @throws photoSpread.PhotoSpreadException.FileNotFoundException *****************************************************/ public static void main(String[] args) throws photoSpread.PhotoSpreadException.FileNotFoundException, FileIOException { Thumbprint tp1; Thumbprint tp2; Thumbprint tp1Again; @SuppressWarnings("unused") Thumbprint tpError; String f1 = "E:\\Users\\Paepcke\\dldev\\src\\PhotoSpreadTesting\\TestCases\\Photos\\crowdOneFaceClear.jpg"; File f2 = new File ("E:\\Users\\Paepcke\\dldev\\src\\PhotoSpreadTesting\\TestCases\\Photos\\conventionCenterTwoWomen.jpg"); tp1 = new Thumbprint(f1); tp2 = new Thumbprint(f2); tp1Again = new Thumbprint(f1); System.out.println("Thumb1 == Thumb2?: " + tp1.equals(tp2)); System.out.println("Thumb1 == Thumb1?: " + tp1.equals(tp1)); System.out.println("Thumb1 == Thumb1Again?: " + tp1.equals(tp1Again)); tpError = new Thumbprint("foo/bar"); } }