Package com.thebuzzmedia.exiftool
Class ExifTool
java.lang.Object
com.thebuzzmedia.exiftool.ExifTool
- All Implemented Interfaces:
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
:
ExifTool tool = new ExifToolBuilder().build();
This mode assume that:
- 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:
ExifTool tool = new ExifToolBuilder()
.withPath("/usr/local/exiftool/bin/exiftool")
.build();
Once created, usage is as simple as making calls to getImageMeta(File, Collection)
or
getImageMeta(File, Format, Collection)
with a list of Tag
you want to pull
values for from the given image.
In this default mode, calls to getImageMeta(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 to getImageMeta(java.io.File)
are returned in a Map
with the StandardTag
values as the keys and String
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 each StandardTag
provides a hint at which format the resulting value
for that tag is returned as from ExifTool (see Tag.parse(String)
), that
only applies to values returned with an output format of
StandardFormat.NUMERIC
and it is ultimately up to the caller to decide how
best to parse or convert the returned values.
The StandardTag
Enum provides the Tag.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 calling
ExifToolBuilder.enableStayOpen()
method.
Calling this method will create an instance of ExifTool
with StayOpenStrategy
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
an UnsupportedFeatureException
if the native
ExifTool doesn't support the requested feature.
In the event of an UnsupportedFeatureException
, 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 (native exiftool
processes) as well as the
read/write streams used to communicate with it unless close()
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 the
exifTool.processCleanupDelay
system variable. A value of 0
or
less disabled the automatic cleanup process and requires you to cleanup
ExifTool instances on your own by calling close()
manually.
You can also set this delay manually using ExifToolBuilder
:
ExifTool exifTool = new ExifToolBuilder()
.enableStayOpen(60000) // Try to clean resources once per minutes.
.build();
Any class activity by way of calls to 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 to
getImageMeta
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 (use ExifToolBuilder.enableStayOpen()
)
and when done, instead of close()
-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 a static 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, the
ProcessBuilder.command(List)
method is used to avoid the copying of
array elements when ProcessBuilder.command(String...)
is used and
avoiding the (hidden) use of StringTokenizer
when
Runtime.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
Modifier and TypeMethodDescriptionvoid
close()
This method should be used to clean previous execution.getImageMeta
(File image) Parse image metadata for all tags.getImageMeta
(File image, ExifToolOptions options) Parse image metadata for all tags.getImageMeta
(File image, ExifToolOptions options, Collection<? extends Tag> tags) Parse image metadata.getImageMeta
(File image, Format format) Parse image metadata for all tags.getImageMeta
(File image, Format format, Collection<? extends Tag> tags) Parse image metadata.getImageMeta
(File image, Collection<? extends Tag> tags) Parse image metadata.Exiftool version pointed by this instance.boolean
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
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 Details
-
close
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
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
Exiftool version pointed by this instance.- Returns:
- Version.
-
getImageMeta
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
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
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
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
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 IOExceptionWrite 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 IOExceptionWrite 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.
-