Package com.thebuzzmedia.exiftool
Class ExifTool
- java.lang.Object
-
- com.thebuzzmedia.exiftool.ExifTool
-
- All Implemented Interfaces:
AutoCloseable
public class ExifTool extends Object implements AutoCloseable
Class used to provide a Java-like interface to Phil Harvey's excellent, Perl-based ExifTool. There are a number of other basic Java wrappers to ExifTool available online, but most of them only abstract out the actual Java-external-process execution logic and do no additional work to make integration with the external ExifTool any easier or intuitive from the perspective of the Java application written to make use of ExifTool. This class was written in order to make integration with ExifTool inside of a Java application seamless and performant with the goal being that the developer can treat ExifTool as if it were written in Java, garnering all of the benefits with none of the added headache of managing an external native process from Java. Phil Harvey's ExifTool is written in Perl and runs on all major platforms (including Windows) so no portability issues are introduced into your application by utilizing this class.Usage
Assuming ExifTool is installed on the host system correctly and either in the system path, using this class to communicate with ExifTool is as simple as creating an instance usingExifToolBuilder
:
This mode assume that:ExifTool tool = new ExifToolBuilder().build();
- Path is set as an environment variable (i.e.
-Dexiftool.withPath=/usr/local/exiftool/bin/exiftool
). - Or globally available.
ExifTool
, you can also specify it during creation:
Once created, usage is as simple as making calls toExifTool tool = new ExifToolBuilder() .withPath("/usr/local/exiftool/bin/exiftool") .build();
getImageMeta(File, Collection)
orgetImageMeta(File, Format, Collection)
with a list ofTag
you want to pull values for from the given image. In this default mode, calls togetImageMeta(java.io.File)
will automatically start an external ExifTool process to handle the request. After ExifTool has parsed the tag values from the file, the external process exits and this class parses the result before returning it to the caller. Results from calls togetImageMeta(java.io.File)
are returned in aMap
with theStandardTag
values as the keys andString
values for every tag that had a value in the image file as the values.StandardTag
s with no value found in the image are omitted from the result map. While eachStandardTag
provides a hint at which format the resulting value for that tag is returned as from ExifTool (seeTag.parse(String)
), that only applies to values returned with an output format ofStandardFormat.NUMERIC
and it is ultimately up to the caller to decide how best to parse or convert the returned values. TheStandardTag
Enum provides theTag.parse(String)
} convenience method for parsing given `String` values according to the Tag hint automatically for you if that is what you plan on doing, otherwise feel free to handle the return values anyway you want.ExifTool -stay_open Support
ExifTool 8.36 added a new persistent-process feature that allows ExifTool to stay running in a daemon mode and continue accepting commands via a file or stdin. This new mode is controlled via the-stay_open True/False
command line argument and in a busy system that is making thousands of calls to ExifTool, can offer speed improvements of up to 60x (yes, really that much). This feature was added to ExifTool shortly after user Christian Etter discovered the overhead for starting up a new Perl interpreter each time ExifTool is loaded accounts for roughly 98.4% of the total runtime. Support for using ExifTool in daemon mode is enabled by explicitly callingExifToolBuilder.enableStayOpen()
method. Calling this method will create an instance ofExifTool
withStayOpenStrategy
execution strategy. Because this feature requires ExifTool 8.36 or later, this class will actually verify support for the feature in the version of ExifTool before successfully instantiating the class and will notify you via anUnsupportedFeatureException
if the native ExifTool doesn't support the requested feature. In the event of anUnsupportedFeatureException
, the caller can either upgrade the native ExifTool upgrade to the version required or simply avoid using that feature to work around the exception.Automatic Resource Cleanup
Whenstay_open
mode is used, there is the potential for leaking both host OS processes (nativeexiftool
processes) as well as the read/write streams used to communicate with it unlessclose()
is called to clean them up when done. Fortunately, this library provides an automatic cleanup mechanism that runs, by default, after 10 minutes of inactivity to clean up those stray resources. The inactivity period can be controlled by modifying theexifTool.processCleanupDelay
system variable. A value of0
or less disabled the automatic cleanup process and requires you to cleanup ExifTool instances on your own by callingclose()
manually. You can also set this delay manually usingExifToolBuilder
:
Any class activity by way of calls toExifTool exifTool = new ExifToolBuilder() .enableStayOpen(60000) // Try to clean resources once per minutes. .build();
getImageMeta
will always reset the inactivity timer, so in a busy system the cleanup thread could potentially never run, leaving the original host ExifTool process running forever (which is fine). This design was chosen to help make using the class and not introducing memory leaks and bugs into your code easier as well as making very inactive instances of this class light weight while not in-use by cleaning up after themselves. The only overhead incurred when opening the process back up is a 250-500ms lag while launching the VM interpreter again on the first call (depending on host machine speed and load).Reusing a "closed" ExifTool Instance
If you or the cleanup thread have calledclose()
on an instance of this class, cleaning up the host process and read/write streams, the instance of this class can still be safely used. Any followup calls togetImageMeta
will simply re-instantiate all the required resources necessary to service the call. This can be handy behavior to be aware of when writing scheduled processing jobs that may wake up every hour and process thousands of pictures then go back to sleep. In order for the process to execute as fast as possible, you would want to use ExifTool in daemon mode (useExifToolBuilder.enableStayOpen()
) and when done, instead ofclose()
-ing the instance of this class and throwing it out, you can keep the reference around and re-use it again when the job executes again an hour later.Performance
Extra care is taken to ensure minimal object creation or unnecessary CPU overhead while communicating with the external process.Pattern
s used to split the responses from the process are explicitly compiled and reused, string concatenation is minimized, Tag name lookup is done via astatic final
Map
shared by all instances and so on. Additionally, extra care is taken to utilize the most optimal code paths when initiating and using the external process, for example, theProcessBuilder.command(List)
method is used to avoid the copying of array elements whenProcessBuilder.command(String...)
is used and avoiding the (hidden) use ofStringTokenizer
whenRuntime.exec(String)
is called. All of this effort was done to ensure that imgscalr and its supporting classes continue to provide best-of-breed performance and memory utilization in long running/high performance environments (e.g. web applications).Thread Safety
Instances of this class are Thread-safe (note that version 1.1 of exiftool was not Thread-safe):- If
stay_open
is disabled, then a one-shot process is used for each command. - Otherwise a single process is open and read/write operations are streamed to this process. In this case, each operation will be synchronized to ensure thread-safety.
ExifTool exifTool = new ExifToolBuilder() .withPoolSize(10) // Allow 10 exiftool process in parallel .build();
Why ExifTool?
ExifTool is written in Perl and requires an external process call from Java to make use of. While this would normally preclude a piece of software from inclusion into the imgscalr library (more complex integration), there is no other image metadata piece of software available as robust, complete and well-tested as ExifTool. In addition, ExifTool already runs on all major platforms (including Windows), so there was not a lack of portability introduced by providing an integration for it. Allowing it to be used from Java is a boon to any Java project that needs the ability to read/write image-metadata from almost any image or video file format.Alternatives
If integration with an external Perl process is something your app cannot do and you still need image metadata-extraction capability, Drew Noakes has written the 2nd most robust image metadata library I have come across: Metadata Extractor that you might want to look at.- Since:
- 1.1
- Author:
- Riyad Kalla ([email protected]), Mickael Jeanroy
-
-
Method Summary
All Methods Instance Methods Concrete Methods Modifier and Type Method Description void
close()
This method should be used to clean previous execution.Map<Tag,String>
getImageMeta(File image)
Parse image metadata for all tags.Map<Tag,String>
getImageMeta(File image, ExifToolOptions options)
Parse image metadata for all tags.Map<Tag,String>
getImageMeta(File image, ExifToolOptions options, Collection<? extends Tag> tags)
Parse image metadata.Map<Tag,String>
getImageMeta(File image, Format format)
Parse image metadata for all tags.Map<Tag,String>
getImageMeta(File image, Format format, Collection<? extends Tag> tags)
Parse image metadata.Map<Tag,String>
getImageMeta(File image, Collection<Tag> tags)
Parse image metadata.Version
getVersion()
Exiftool version pointed by this instance.boolean
isRunning()
This method is used to determine if there is currently a running ExifTool process associated with this class.void
setImageMeta(File image, ExifToolOptions options, Map<? extends Tag,String> tags)
Write image metadata in a specific format.void
setImageMeta(File image, Format format, Map<? extends Tag,String> tags)
Write image metadata in a specific format.void
setImageMeta(File image, Map<? extends Tag,String> tags)
Write image metadata.void
stop()
Stop `ExifTool` client.
-
-
-
Method Detail
-
close
public void close() throws Exception
This method should be used to clean previous execution.
NOTE: Calling this method prevent this instance ofExifTool
from being re-used.- Specified by:
close
in interfaceAutoCloseable
- Throws:
Exception
- If an error occurred while closing exiftool client.
-
stop
public void stop() throws Exception
Stop `ExifTool` client. NOTE: Calling this method does not preclude this instance ofExifTool
from being re-used, it merely disposes of the native and internal resources until the next call togetImageMeta
causes them to be re-instantiated.- Throws:
Exception
- If an error occurred while stopping exiftool client.
-
isRunning
public boolean isRunning()
This method is used to determine if there is currently a running ExifTool process associated with this class.
Any dependent processes and streams can be shutdown usingclose()
and this class will automatically re-create them on the next call togetImageMeta(java.io.File)
if necessary.- Returns:
true
if there is an external ExifTool process is still running otherwise returnsfalse
.
-
getVersion
public Version getVersion()
Exiftool version pointed by this instance.- Returns:
- Version.
-
getImageMeta
public Map<Tag,String> getImageMeta(File image) throws IOException
Parse image metadata for all tags. Output format is numeric.- Parameters:
image
- Image.- Returns:
- Pair of tag associated with the value.
- Throws:
IOException
- If something bad happen during I/O operations.NullPointerException
- If one parameter is null.IllegalArgumentException
- If list of tag is empty.UnreadableFileException
- If image cannot be read.
-
getImageMeta
public Map<Tag,String> getImageMeta(File image, Format format) throws IOException
Parse image metadata for all tags.- Parameters:
image
- Image.format
- Output format.- Returns:
- Pair of tag associated with the value.
- Throws:
IOException
- If something bad happen during I/O operations.NullPointerException
- If one parameter is null.IllegalArgumentException
- If list of tag is empty.UnreadableFileException
- If image cannot be read.
-
getImageMeta
public Map<Tag,String> getImageMeta(File image, ExifToolOptions options) throws IOException
Parse image metadata for all tags.- Parameters:
image
- Image.options
- ExifTool options.- Returns:
- Pair of tag associated with the value.
- Throws:
IOException
- If something bad happen during I/O operations.NullPointerException
- If one parameter is null.IllegalArgumentException
- If list of tag is empty.UnreadableFileException
- If image cannot be read.
-
getImageMeta
public Map<Tag,String> getImageMeta(File image, Collection<Tag> tags) throws IOException
Parse image metadata. Output format is numeric.- Parameters:
image
- Image.tags
- List of tags to extract.- Returns:
- Pair of tag associated with the value.
- Throws:
IOException
- If something bad happen during I/O operations.NullPointerException
- If one parameter is null.IllegalArgumentException
- If list of tag is empty.UnreadableFileException
- If image cannot be read.
-
getImageMeta
public Map<Tag,String> getImageMeta(File image, Format format, Collection<? extends Tag> tags) throws IOException
Parse image metadata.- Parameters:
image
- Image.format
- Output format.tags
- List of tags to extract.- Returns:
- Pair of tag associated with the value.
- Throws:
IOException
- If something bad happen during I/O operations.NullPointerException
- If one parameter is null.IllegalArgumentException
- If list of tag is empty.UnreadableFileException
- If image cannot be read.
-
getImageMeta
public Map<Tag,String> getImageMeta(File image, ExifToolOptions options, Collection<? extends Tag> tags) throws IOException
Parse image metadata.- Parameters:
image
- Image.options
- ExifTool options.tags
- List of tags to extract.- Returns:
- Pair of tag associated with the value.
- Throws:
IOException
- If something bad happen during I/O operations.NullPointerException
- If one parameter is null.IllegalArgumentException
- If list of tag is empty.UnreadableFileException
- If image cannot be read.
-
setImageMeta
public void setImageMeta(File image, Map<? extends Tag,String> tags) throws IOException
Write image metadata. Default format is numeric.- Parameters:
image
- Image.tags
- Tags to write.- Throws:
IOException
- If an error occurs during write operation.
-
setImageMeta
public void setImageMeta(File image, Format format, Map<? extends Tag,String> tags) throws IOException
Write image metadata in a specific format.- Parameters:
image
- Image.format
- Specified format.tags
- Tags to write.- Throws:
IOException
- If an error occurs during write operation.
-
setImageMeta
public void setImageMeta(File image, ExifToolOptions options, Map<? extends Tag,String> tags) throws IOException
Write image metadata in a specific format.- Parameters:
image
- Image.options
- ExifTool options.tags
- Tags to write.- Throws:
IOException
- If an error occurs during write operation.
-
-