From 4f406cacaa682dd692f4c80b914dab0db0164d82 Mon Sep 17 00:00:00 2001 From: "Ole B. Rosentreter" Date: Sat, 28 Sep 2024 21:57:02 +0200 Subject: [PATCH 1/1] Initial project version --- pom.xml | 90 ++++++ .../product/hashservice/ImageCrawler.java | 201 +++++++++++++ .../obgit/product/hashservice/ImageDao.java | 38 +++ .../product/hashservice/ImageEntity.java | 279 ++++++++++++++++++ .../obgit/product/hashservice/MimeType.java | 34 +++ .../product/hashservice/Orientation.java | 5 + .../product/hashservice/logging/Loglevel.java | 6 + .../hashservice/logging/StaticLogger.java | 41 +++ src/main/resources/META-INF/persistence.xml | 22 ++ src/main/resources/log4j.xml | 27 ++ 10 files changed, 743 insertions(+) create mode 100644 pom.xml create mode 100644 src/main/java/de/obgit/product/hashservice/ImageCrawler.java create mode 100644 src/main/java/de/obgit/product/hashservice/ImageDao.java create mode 100644 src/main/java/de/obgit/product/hashservice/ImageEntity.java create mode 100644 src/main/java/de/obgit/product/hashservice/MimeType.java create mode 100644 src/main/java/de/obgit/product/hashservice/Orientation.java create mode 100644 src/main/java/de/obgit/product/hashservice/logging/Loglevel.java create mode 100644 src/main/java/de/obgit/product/hashservice/logging/StaticLogger.java create mode 100644 src/main/resources/META-INF/persistence.xml create mode 100644 src/main/resources/log4j.xml diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..1a54271 --- /dev/null +++ b/pom.xml @@ -0,0 +1,90 @@ + + 4.0.0 + de.laktatnebel.tools.hashservice + imagehash + 1.0.0-SNAPSHOT + + ImageHash + + + de.laktatnebel.maven + laktatnebelproductstandalone + 2.0.0 + + + + 1.2.3 + + + + + + + de.laktatnebel.libs + filelib + ${filelib.version} + + + org.apache.commons + commons-io + 1.3.2 + + + commons-codec + commons-codec + 1.10 + + + com.drewnoakes + metadata-extractor + 2.9.1 + + + org.apache.openjpa + openjpa + 2.4.1 + + + postgresql + postgresql + 8.4-702.jdbc4 + + + + + + + de.laktatnebel.libs + filelib + + + org.apache.commons + commons-io + + + commons-codec + commons-codec + + + com.drewnoakes + metadata-extractor + + + org.apache.openjpa + openjpa + + + postgresql + postgresql + + + org.slf4j + slf4j-api + + + org.slf4j + slf4j-log4j12 + + + \ No newline at end of file diff --git a/src/main/java/de/obgit/product/hashservice/ImageCrawler.java b/src/main/java/de/obgit/product/hashservice/ImageCrawler.java new file mode 100644 index 0000000..0dfbbe9 --- /dev/null +++ b/src/main/java/de/obgit/product/hashservice/ImageCrawler.java @@ -0,0 +1,201 @@ +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 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)); + } +} diff --git a/src/main/java/de/obgit/product/hashservice/ImageDao.java b/src/main/java/de/obgit/product/hashservice/ImageDao.java new file mode 100644 index 0000000..a70c8ae --- /dev/null +++ b/src/main/java/de/obgit/product/hashservice/ImageDao.java @@ -0,0 +1,38 @@ +package de.obgit.product.hashservice; + +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.EntityTransaction; +import javax.persistence.Persistence; + +public class ImageDao { + EntityManagerFactory emf; + + public void createEntity(final T entity) { + emf = Persistence.createEntityManagerFactory("de.obgit.product.imagehash.PU"); + final EntityManager em = emf.createEntityManager(); + final EntityTransaction tx = em.getTransaction(); + try { + tx.begin(); + em.persist(entity); + tx.commit(); + } catch (final RuntimeException ex) { + if ((tx != null) && tx.isActive()) { + tx.rollback(); + } + throw ex; + } finally { + em.clear(); + em.close(); + } + } + + public T readEntity(final Class clss, final Object id) { + final EntityManager em = emf.createEntityManager(); + try { + return em.find(clss, id); + } finally { + em.close(); + } + } +} diff --git a/src/main/java/de/obgit/product/hashservice/ImageEntity.java b/src/main/java/de/obgit/product/hashservice/ImageEntity.java new file mode 100644 index 0000000..6c7eb7a --- /dev/null +++ b/src/main/java/de/obgit/product/hashservice/ImageEntity.java @@ -0,0 +1,279 @@ +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; + + } + +} diff --git a/src/main/java/de/obgit/product/hashservice/MimeType.java b/src/main/java/de/obgit/product/hashservice/MimeType.java new file mode 100644 index 0000000..61a3f7c --- /dev/null +++ b/src/main/java/de/obgit/product/hashservice/MimeType.java @@ -0,0 +1,34 @@ +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; + } + +} diff --git a/src/main/java/de/obgit/product/hashservice/Orientation.java b/src/main/java/de/obgit/product/hashservice/Orientation.java new file mode 100644 index 0000000..2b923df --- /dev/null +++ b/src/main/java/de/obgit/product/hashservice/Orientation.java @@ -0,0 +1,5 @@ +package de.obgit.product.hashservice; + +public enum Orientation { + LANDSCAPE, PORTRAIT +} diff --git a/src/main/java/de/obgit/product/hashservice/logging/Loglevel.java b/src/main/java/de/obgit/product/hashservice/logging/Loglevel.java new file mode 100644 index 0000000..f60a529 --- /dev/null +++ b/src/main/java/de/obgit/product/hashservice/logging/Loglevel.java @@ -0,0 +1,6 @@ +package de.obgit.product.hashservice.logging; + +public enum Loglevel { + + ALL, TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF; +} diff --git a/src/main/java/de/obgit/product/hashservice/logging/StaticLogger.java b/src/main/java/de/obgit/product/hashservice/logging/StaticLogger.java new file mode 100644 index 0000000..69e8681 --- /dev/null +++ b/src/main/java/de/obgit/product/hashservice/logging/StaticLogger.java @@ -0,0 +1,41 @@ +package de.obgit.product.hashservice.logging; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class StaticLogger { + /** Logger logger field */ + private static Logger logger = LoggerFactory.getLogger(StaticLogger.class); + + { + logger = null; + } + /** + * + * @return + */ + public static Logger getLogger(Class clazz) { + if (StaticLogger.logger == null) { + StaticLogger.logger = LoggerFactory.getLogger(clazz); + } + return StaticLogger.logger; + } + /** + * + * @param logger + * @param string + */ + public static void log(Logger logger, String string) { + if (logger.isTraceEnabled()) { + logger.trace(string); + } else if (logger.isDebugEnabled()) { + logger.debug(string); + } else if (logger.isInfoEnabled()) { + logger.info(string); + } else if (logger.isWarnEnabled()) { + logger.warn(string); + } else if (logger.isErrorEnabled()) { + logger.error(string); + } + } +} diff --git a/src/main/resources/META-INF/persistence.xml b/src/main/resources/META-INF/persistence.xml new file mode 100644 index 0000000..87db8d8 --- /dev/null +++ b/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,22 @@ + + + + org.apache.openjpa.persistence.PersistenceProviderImpl + de.obgit.product.hashservice.ImageEntity + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/log4j.xml b/src/main/resources/log4j.xml new file mode 100644 index 0000000..e4f3264 --- /dev/null +++ b/src/main/resources/log4j.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + -- 2.39.5