--- /dev/null
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>de.laktatnebel.tools.hashservice</groupId>
+ <artifactId>imagehash</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+
+ <name>ImageHash</name>
+
+ <parent>
+ <groupId>de.laktatnebel.maven</groupId>
+ <artifactId>laktatnebelproductstandalone</artifactId>
+ <version>2.0.0</version>
+ </parent>
+
+ <properties>
+ <filelib.version>1.2.3</filelib.version>
+ </properties>
+
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>de.laktatnebel.libs</groupId>
+ <artifactId>filelib</artifactId>
+ <version>${filelib.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>1.3.2</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ <version>1.10</version>
+ </dependency>
+ <dependency>
+ <groupId>com.drewnoakes</groupId>
+ <artifactId>metadata-extractor</artifactId>
+ <version>2.9.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.openjpa</groupId>
+ <artifactId>openjpa</artifactId>
+ <version>2.4.1</version>
+ </dependency>
+ <dependency>
+ <groupId>postgresql</groupId>
+ <artifactId>postgresql</artifactId>
+ <version>8.4-702.jdbc4</version>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+ <dependency>
+ <groupId>de.laktatnebel.libs</groupId>
+ <artifactId>filelib</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-io</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.drewnoakes</groupId>
+ <artifactId>metadata-extractor</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.openjpa</groupId>
+ <artifactId>openjpa</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>postgresql</groupId>
+ <artifactId>postgresql</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
--- /dev/null
+package de.obgit.product.hashservice;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang.StringUtils;
+
+import com.drew.imaging.ImageMetadataReader;
+import com.drew.imaging.ImageProcessingException;
+import com.drew.metadata.Directory;
+import com.drew.metadata.Metadata;
+import com.drew.metadata.Tag;
+import com.drew.metadata.bmp.BmpHeaderDirectory;
+import com.drew.metadata.exif.ExifIFD0Descriptor;
+import com.drew.metadata.exif.ExifIFD0Directory;
+import com.drew.metadata.gif.GifHeaderDirectory;
+import com.drew.metadata.jpeg.JpegDescriptor;
+import com.drew.metadata.jpeg.JpegDirectory;
+import com.drew.metadata.png.PngDirectory;
+
+import de.obgit.libs.filelib.DirectoryManager;
+import de.obgit.libs.filelib.exception.AbstractFileLibException;
+import de.obgit.product.hashservice.logging.Loglevel;
+import de.obgit.product.hashservice.logging.StaticLogger;
+
+public class ImageCrawler {
+
+ private static final String WHITESPACE = " ";
+
+ public static void main(final String[] args) throws AbstractFileLibException {
+
+ final Path startPath = new File(args[0]).toPath();
+ final String mime = args[1];
+ final List<Path> pathListRecursivlyFromPath = DirectoryManager.getPathListRecursivlyFromPath(startPath, mime);
+
+ final ImageDao id = new ImageDao();
+
+ for (final Path path : pathListRecursivlyFromPath) {
+ final String pathToString = path.toString();
+ StaticLogger.getLogger(ImageCrawler.class).info(pathToString);
+ final ImageEntity imageEntity = new ImageEntity();
+
+ final File file = path.toFile();
+ getFileNameDetails(imageEntity, file);
+
+ try {
+ getFileDetails(path, imageEntity);
+
+ final FileInputStream fis = new FileInputStream(file);
+ imageEntity.setMd5(DigestUtils.md5Hex(fis));
+ fis.close();
+ } catch (final IOException ioe) {
+ StaticLogger.getLogger(ImageCrawler.class).error(ioe.getMessage());
+ }
+
+ Metadata metadata = null;
+
+ try {
+ metadata = ImageMetadataReader.readMetadata(new File(pathToString));
+
+ getAllTags(metadata, Loglevel.TRACE);
+
+ StaticLogger.getLogger(ImageCrawler.class).debug(imageEntity.getMimetype().getMimeType());
+
+ String imageHeightDescription = null;
+ String imageWidthDescription = null;
+ Date dateTime = null;
+
+ switch (imageEntity.getMimetype()) {
+ case JPEG:
+ final JpegDescriptor jpegDescriptor = new JpegDescriptor(metadata.getFirstDirectoryOfType(JpegDirectory.class));
+
+ imageHeightDescription = jpegDescriptor.getDescription(JpegDirectory.TAG_IMAGE_HEIGHT);
+ imageWidthDescription = jpegDescriptor.getDescription(JpegDirectory.TAG_IMAGE_WIDTH);
+
+ final ExifIFD0Directory exifIFD0Directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
+ if (exifIFD0Directory != null) {
+ dateTime = exifIFD0Directory.getDate(ExifIFD0Directory.TAG_DATETIME);
+ }
+
+ break;
+ case GIF:
+ final GifHeaderDirectory gifHeaderDirectory = metadata.getFirstDirectoryOfType(GifHeaderDirectory.class);
+
+ imageHeightDescription = gifHeaderDirectory.getDescription(GifHeaderDirectory.TAG_IMAGE_HEIGHT);
+ imageWidthDescription = gifHeaderDirectory.getDescription(GifHeaderDirectory.TAG_IMAGE_WIDTH);
+
+ getAllTags(metadata, Loglevel.INFO);
+ break;
+ case TIFF:
+
+ final ExifIFD0Directory exifIFD0DirectoryTiff = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
+ final ExifIFD0Descriptor exifDescriptor = new ExifIFD0Descriptor(exifIFD0DirectoryTiff);
+
+ imageHeightDescription = exifDescriptor.getDescription(ExifIFD0Directory.TAG_IMAGE_HEIGHT);
+ imageWidthDescription = exifDescriptor.getDescription(ExifIFD0Directory.TAG_IMAGE_WIDTH);
+
+ dateTime = exifIFD0DirectoryTiff.getDate(ExifIFD0Directory.TAG_DATETIME);
+
+ getAllTags(metadata, Loglevel.INFO);
+
+ break;
+ case PNG:
+ final PngDirectory pngDirectory = metadata.getFirstDirectoryOfType(PngDirectory.class);
+
+ imageHeightDescription = pngDirectory.getDescription(PngDirectory.TAG_IMAGE_HEIGHT);
+ imageWidthDescription = pngDirectory.getDescription(PngDirectory.TAG_IMAGE_WIDTH);
+
+ getAllTags(metadata, Loglevel.INFO);
+
+ break;
+ case BMP:
+ final BmpHeaderDirectory bmpDirectory = metadata.getFirstDirectoryOfType(BmpHeaderDirectory.class);
+
+ imageHeightDescription = bmpDirectory.getDescription(BmpHeaderDirectory.TAG_IMAGE_HEIGHT);
+ imageWidthDescription = bmpDirectory.getDescription(BmpHeaderDirectory.TAG_IMAGE_WIDTH);
+
+ getAllTags(metadata, Loglevel.INFO);
+
+ break;
+ default:
+ break;
+ }
+
+ StaticLogger.getLogger(ImageCrawler.class).debug(imageHeightDescription);
+ StaticLogger.getLogger(ImageCrawler.class).debug(imageWidthDescription);
+
+ if (!imageHeightDescription.equals(null)) {
+ imageEntity.setHeight(Integer.valueOf(StringUtils.substringBefore(imageHeightDescription, WHITESPACE)));
+ }
+ if (!imageWidthDescription.equals(null)) {
+ imageEntity.setWidth(Integer.valueOf(StringUtils.substringBefore(imageWidthDescription, WHITESPACE)));
+ }
+ imageEntity.setOrientation(Orientation.PORTRAIT);
+ if (imageEntity.getHeight().intValue() < imageEntity.getWidth().intValue()) {
+ imageEntity.setOrientation(Orientation.LANDSCAPE);
+ }
+ imageEntity.setExposureDate(dateTime);
+
+ } catch (final ImageProcessingException ipe) {
+ getAllTags(metadata, Loglevel.ERROR);
+ StaticLogger.getLogger(ImageCrawler.class).error(ipe.getMessage());
+ } catch (final IOException ioe) {
+ getAllTags(metadata, Loglevel.ERROR);
+ StaticLogger.getLogger(ImageCrawler.class).error(ioe.getMessage());
+ } catch (final NullPointerException npe) {
+ getAllTags(metadata, Loglevel.ERROR);
+ StaticLogger.getLogger(ImageCrawler.class).error(npe.getMessage());
+ }
+
+ StaticLogger.getLogger(ImageCrawler.class).info(imageEntity.toString());
+
+ id.createEntity(imageEntity);
+
+ }
+ }
+
+ private static void getAllTags(final Metadata metadata, final Loglevel logLevel) {
+ if ((metadata == null) || (metadata.getDirectoryCount() == 0)) {
+ StaticLogger.getLogger(ImageCrawler.class).info("Keine Metadaten vorhanden!");
+ return;
+ }
+ for (final Directory directory : metadata.getDirectories()) {
+ for (final Tag tag : directory.getTags()) {
+ if (logLevel.equals(Loglevel.ERROR)) {
+ StaticLogger.getLogger(ImageCrawler.class).error(String.format("%s - [%s] - %s = %s\n", directory.getClass(), directory.getName(), tag.getTagName(), tag.getDescription()));
+ } else {
+ StaticLogger.getLogger(ImageCrawler.class).debug(String.format("%s - [%s] - %s = %s\n", directory.getClass(), directory.getName(), tag.getTagName(), tag.getDescription()));
+ }
+
+ }
+ if (directory.hasErrors()) {
+ for (final String error : directory.getErrors()) {
+ StaticLogger.getLogger(ImageCrawler.class).error(String.format("ERROR: %s", error));
+ }
+ }
+ }
+ }
+
+ private static void getFileDetails(final Path path, final ImageEntity imageEntity) throws IOException {
+ imageEntity.setMimetype(MimeType.getMimeTypeFromValue(Files.probeContentType(path)));
+ imageEntity.setFilesize(Files.size(path));
+ imageEntity.setLastModifiedDate(new Date(Files.getLastModifiedTime(path, LinkOption.NOFOLLOW_LINKS).toMillis()));
+ }
+
+ private static void getFileNameDetails(final ImageEntity imageEntity, final File file) {
+ final String filename = file.getAbsolutePath();
+
+ imageEntity.setExtension(FilenameUtils.getExtension(filename));
+ imageEntity.setBasename(FilenameUtils.getBaseName(filename));
+ imageEntity.setPath(FilenameUtils.getFullPath(filename));
+ }
+}
--- /dev/null
+package de.obgit.product.hashservice;\r
+\r
+import javax.persistence.EntityManager;\r
+import javax.persistence.EntityManagerFactory;\r
+import javax.persistence.EntityTransaction;\r
+import javax.persistence.Persistence;\r
+\r
+public class ImageDao {\r
+ EntityManagerFactory emf;\r
+\r
+ public <T> void createEntity(final T entity) {\r
+ emf = Persistence.createEntityManagerFactory("de.obgit.product.imagehash.PU");\r
+ final EntityManager em = emf.createEntityManager();\r
+ final EntityTransaction tx = em.getTransaction();\r
+ try {\r
+ tx.begin();\r
+ em.persist(entity);\r
+ tx.commit();\r
+ } catch (final RuntimeException ex) {\r
+ if ((tx != null) && tx.isActive()) {\r
+ tx.rollback();\r
+ }\r
+ throw ex;\r
+ } finally {\r
+ em.clear();\r
+ em.close();\r
+ }\r
+ }\r
+\r
+ public <T> T readEntity(final Class<T> clss, final Object id) {\r
+ final EntityManager em = emf.createEntityManager();\r
+ try {\r
+ return em.find(clss, id);\r
+ } finally {\r
+ em.close();\r
+ }\r
+ }\r
+}\r
--- /dev/null
+package de.obgit.product.hashservice;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+
+@Entity(name = "album")
+public class ImageEntity implements Serializable {
+
+ private static final long serialVersionUID = -1927473689827557891L;
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ @Column(name = "id")
+ private Long id;
+
+ @Column(name = "md5")
+ private String md5;
+
+ @Column(name = "filesize")
+ private long filesize;
+
+ @Column(name = "basename")
+ private String basename;
+
+ @Column(name = "extension")
+ private String extension;
+
+ @Column(name = "path")
+ private String path;
+
+ @Column(name = "mimetype")
+ private String mimetype;
+
+ @Column(name = "height")
+ private Integer height;
+
+ @Column(name = "width")
+ private Integer width;
+
+ @Column(name = "orientation")
+ private String orientation;
+
+ @Column(name = "lastmodifieddate")
+ private Date lastModifiedDate;
+
+ @Column(name = "exposuredate")
+ private Date exposureDate;
+
+ /**
+ *
+ */
+ public ImageEntity() {
+ super();
+ // TODO Auto-generated constructor stub
+ }
+
+ /**
+ * @return the id
+ */
+ public Long getId() {
+ return id;
+ }
+
+ /**
+ * @param id
+ * the id to set
+ */
+ public void setId(final Long id) {
+ this.id = id;
+ }
+
+ /**
+ * @return the md5
+ */
+ public String getMd5() {
+ return md5;
+ }
+
+ /**
+ * @param md5
+ * the md5 to set
+ */
+ public void setMd5(final String md5) {
+ this.md5 = md5;
+ }
+
+ /**
+ * @return the filesize
+ */
+ public long getFilesize() {
+ return filesize;
+ }
+
+ /**
+ * @param filesize
+ * the filesize to set
+ */
+ public void setFilesize(final long filesize) {
+ this.filesize = filesize;
+ }
+
+ /**
+ * @return the basename
+ */
+ public String getBasename() {
+ return basename;
+ }
+
+ /**
+ * @param basename
+ * the basename to set
+ */
+ public void setBasename(final String basename) {
+ this.basename = basename;
+ }
+
+ /**
+ * @return the extension
+ */
+ public String getExtension() {
+ return extension;
+ }
+
+ /**
+ * @param extension
+ * the extension to set
+ */
+ public void setExtension(final String extension) {
+ this.extension = extension;
+ }
+
+ /**
+ * @return the path
+ */
+ public String getPath() {
+ return path;
+ }
+
+ /**
+ * @param path
+ * the path to set
+ */
+ public void setPath(final String path) {
+ this.path = path;
+ }
+
+ /**
+ * @return the mimetype
+ */
+ public MimeType getMimetype() {
+ return MimeType.getMimeTypeFromValue(mimetype);
+ }
+
+ /**
+ * @param mimetype
+ * the mimetype to set
+ */
+ public void setMimetype(final MimeType mimetype) {
+ this.mimetype = mimetype.getMimeType();
+ }
+
+ /**
+ * @return the height
+ */
+ public Integer getHeight() {
+ return height;
+ }
+
+ /**
+ * @param height
+ * the height to set
+ */
+ public void setHeight(final Integer height) {
+ this.height = height;
+ }
+
+ /**
+ * @return the width
+ */
+ public Integer getWidth() {
+ return width;
+ }
+
+ /**
+ * @param width
+ * the width to set
+ */
+ public void setWidth(final Integer width) {
+ this.width = width;
+ }
+
+ /**
+ * @return the orientation
+ */
+ public Orientation getOrientation() {
+ return Orientation.valueOf(orientation);
+ }
+
+ /**
+ * @param orientation
+ * the orientation to set
+ */
+ public void setOrientation(final Orientation orientation) {
+ this.orientation = orientation.name();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append("ImageEntity [id=");
+ builder.append(id);
+ builder.append(", md5=");
+ builder.append(md5);
+ builder.append(", filesize=");
+ builder.append(filesize);
+ builder.append(", basename=");
+ builder.append(basename);
+ builder.append(", extension=");
+ builder.append(extension);
+ builder.append(", path=");
+ builder.append(path);
+ builder.append(", mimetype=");
+ builder.append(mimetype);
+ builder.append(", height=");
+ builder.append(height);
+ builder.append(", width=");
+ builder.append(width);
+ builder.append(", orientation=");
+ builder.append(orientation);
+ builder.append(", lastModifiedDate=");
+ builder.append(lastModifiedDate);
+ builder.append(", exposureDate=");
+ builder.append(exposureDate);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /**
+ * @return the exposureDate
+ */
+ public Date getExposureDate() {
+ return exposureDate;
+ }
+
+ /**
+ * @param exposureDate
+ * the exposureDate to set
+ */
+ public void setExposureDate(final Date exposureDate) {
+ this.exposureDate = exposureDate;
+ }
+
+ /**
+ * @return the lastModifiedDate
+ */
+ public Date getLastModifiedDate() {
+ return lastModifiedDate;
+ }
+
+ /**
+ * @param lastModifiedDate
+ */
+ public void setLastModifiedDate(final Date lastModifiedDate) {
+ this.lastModifiedDate = lastModifiedDate;
+
+ }
+
+}
--- /dev/null
+package de.obgit.product.hashservice;
+
+public enum MimeType {
+
+ JPEG("image/jpeg"), GIF("image/gif"), TIFF("image/tiff"), PNG("image/png"), BMP("image/bmp"), OTHER("image/other");
+
+ private String mimeType;
+
+ /**
+ * @param name
+ * @param ordinal
+ */
+ MimeType(final String mimeType) {
+ setMimeType(mimeType);
+ }
+
+ public String getMimeType() {
+ return mimeType;
+ }
+
+ public void setMimeType(final String mimeType) {
+ this.mimeType = mimeType;
+ }
+
+ static MimeType getMimeTypeFromValue(final String mimeTypeString) {
+ for (final MimeType t : MimeType.values()) {
+ if (t.getMimeType().equals(mimeTypeString)) {
+ return t;
+ }
+ }
+ return OTHER;
+ }
+
+}
--- /dev/null
+package de.obgit.product.hashservice;\r
+\r
+public enum Orientation {\r
+ LANDSCAPE, PORTRAIT\r
+}\r
--- /dev/null
+package de.obgit.product.hashservice.logging;\r
+\r
+public enum Loglevel {\r
+\r
+ ALL, TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF;\r
+}\r
--- /dev/null
+package de.obgit.product.hashservice.logging;\r
+\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+public class StaticLogger {\r
+ /** Logger logger field */\r
+ private static Logger logger = LoggerFactory.getLogger(StaticLogger.class);\r
+\r
+ {\r
+ logger = null;\r
+ }\r
+ /**\r
+ * \r
+ * @return\r
+ */\r
+ public static Logger getLogger(Class<?> clazz) {\r
+ if (StaticLogger.logger == null) {\r
+ StaticLogger.logger = LoggerFactory.getLogger(clazz);\r
+ }\r
+ return StaticLogger.logger;\r
+ }\r
+ /**\r
+ *\r
+ * @param logger\r
+ * @param string\r
+ */\r
+ public static void log(Logger logger, String string) {\r
+ if (logger.isTraceEnabled()) {\r
+ logger.trace(string);\r
+ } else if (logger.isDebugEnabled()) {\r
+ logger.debug(string);\r
+ } else if (logger.isInfoEnabled()) {\r
+ logger.info(string);\r
+ } else if (logger.isWarnEnabled()) {\r
+ logger.warn(string);\r
+ } else if (logger.isErrorEnabled()) {\r
+ logger.error(string);\r
+ }\r
+ }\r
+}\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<persistence version="1.0"\r
+ xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+ xsi:schemaLocation="http://java.sun.com/xml/ns/persistence\r
+ http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">\r
+ <persistence-unit name="de.obgit.product.imagehash.PU"\r
+ transaction-type="RESOURCE_LOCAL">\r
+ <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>\r
+ <class>de.obgit.product.hashservice.ImageEntity</class>\r
+ <properties>\r
+ <property name="openjpa.ConnectionURL" value="jdbc:postgresql://localhost:5432/images" />\r
+ <property name="openjpa.ConnectionDriverName" value="org.postgresql.Driver" />\r
+ <property name="openjpa.ConnectionUserName" value="oleb" />\r
+ <property name="openjpa.ConnectionPassword" value="wpj_9+L6ukX+SN2-" />\r
+ <property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema" />\r
+ <property name="openjpa.jdbc.DBDictionary" value="postgres" />\r
+ <property\r
+ name="javax.persistence.schema-generation.create-database-schemas"\r
+ value="true" />\r
+ </properties>\r
+ </persistence-unit>\r
+</persistence>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>\r
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">\r
+\r
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">\r
+\r
+ <appender name="console" class="org.apache.log4j.ConsoleAppender">\r
+ <layout class="org.apache.log4j.PatternLayout">\r
+ <param name="ConversionPattern" value="%d [%t] %p - %m%n" />\r
+ </layout>\r
+ </appender>\r
+\r
+ <appender name="logfile" class="org.apache.log4j.ConsoleAppender">\r
+ <param name="datePattern" value="'.'yyyy-MM-dd_HH-mm" />\r
+ <param name="file" value="imagehash.log" />\r
+ <param name="Append" value="true" />\r
+ <layout class="org.apache.log4j.PatternLayout">\r
+ <param name="ConversionPattern" value="%d [%t] %p - %m%n" />\r
+ </layout>\r
+ </appender>\r
+\r
+ <root>\r
+ <priority value="info" />\r
+ <appender-ref ref="console" />\r
+<!-- <appender-ref ref="logfile" /> -->\r
+ </root>\r
+\r
+</log4j:configuration>\r