| Crates.io | exiftool |
| lib.rs | exiftool |
| version | 0.2.6 |
| created_at | 2025-04-06 20:44:50.970478+00 |
| updated_at | 2025-08-02 12:02:43.117722+00 |
| description | A Rust wrapper for ExifTool. |
| homepage | https://github.com/ruurdbijlsma/exiftool_rs |
| repository | https://github.com/ruurdbijlsma/exiftool_rs |
| max_upload_size | |
| id | 1623378 |
| size | 172,356 |
Rust wrapper for Phil Harvey's ExifTool command-line application.
This crate interacts with a persistent exiftool process using the -stay_open argument, significantly reducing
overhead compared to spawning a new process for each command.
Note: This crate assumes that the exiftool command-line executable is available already, via PATH or by passing an
executable.
exiftool process (-stay_open) for minimal overhead per command.ExifToolError), and serde support for
deserialization.serde_json::Value).ExifData.You must have Phil Harvey's ExifTool command-line utility installed and accessible in your system's PATH.
brew install exiftoolsudo apt install libimage-exiftool-perlVerify your installation by typing exiftool -ver in your terminal.
use exiftool::{ExifTool, ExifToolError};
use std::path::Path;
fn main() -> Result<(), ExifToolError> {
let mut exiftool = ExifTool::new()?;
let path = Path::new("data/image.jpg");
// Read a tag (String)
let make: String = exiftool.read_tag(path, "Make")?;
println!("Make (String): {}", make); // Output: Make (String): Huawei
// Read a required tag (u32)
let width: u32 = exiftool.read_tag(path, "ImageWidth")?;
println!("Width (u32): {}", width); // Output: Width (u32): 2688
// Read an optional tag that is missing
let desc: Option<String> = exiftool.read_tag(path, "ImageDescription")?;
println!("Description: {:?}", desc); // Output: Description: None
Ok(())
}
Value)use exiftool::{ExifTool, ExifToolError};
use std::path::Path;
fn main() -> Result<(), ExifToolError> {
let mut exiftool = ExifTool::new()?;
let path = Path::new("data/image.jpg");
// Get all metadata, grouped by category (image, audio, video, camera, etc.)
let metadata_json = exiftool.json(path, &["-g2"])?;
println!("All Metadata JSON (-g1 -common):\n{:#}", metadata_json);
Ok(())
}
There's a provided struct (ExifData) for dealing with common fields, if you want that type safety. -g2 has to be
used to use this struct.
use exiftool::{ExifTool, ExifToolError, ExifData};
use std::path::Path;
fn main() -> Result<(), ExifToolError> {
let mut exiftool = ExifTool::new()?;
let path = Path::new("data/image.jpg");
// Use -g2 for the structure expected by the ExifData type
let exif_data: ExifData = exiftool.read_metadata(path, &["-g2"])?;
println!("Parsed ExifData:\n{:#?}", exif_data);
if let Some(camera_meta) = exif_data.camera {
println!("Camera Make: {:?}", camera_meta.make);
println!("Camera Model: {:?}", camera_meta.model);
}
if let Some(other_meta) = exif_data.other {
println!("File Name: {:?}", other_meta.file_name);
println!("MIME Type: {:?}", other_meta.mime_type);
}
Ok(())
}
use exiftool::{ExifTool, ExifToolError};
use std::path::Path;
fn main() -> Result<(), ExifToolError> {
let mut exiftool = ExifTool::new()?;
let paths = [
Path::new("data/image.jpg"),
Path::new("data/other_images/jpg/gps/DSCN0010.jpg")
];
// Get specific tags for multiple files, if you want all tags, leave the `extra_args` empty.
let results = exiftool.json_batch(&paths, &["-FileName", "-FileSize", "-ImageWidth"])?;
for metadata_value in results {
println!("--- File: {} ---", metadata_value.get("SourceFile").and_then(|v| v.as_str()).unwrap_or("N/A"));
println!("{:#}", metadata_value);
}
Ok(())
}
use exiftool::{ExifTool, ExifToolError};
use std::path::Path;
use std::fs;
fn main() -> Result<(), ExifToolError> {
let mut exiftool = ExifTool::new()?;
let path = Path::new("data/image.jpg");
// Extract the thumbnail image
let thumb_bytes = exiftool.read_tag_binary(path, "ThumbnailImage")?;
println!("Read {} bytes for ThumbnailImage", thumb_bytes.len());
// Optional: Save the thumbnail
fs::write("thumbnail.jpg", &thumb_bytes)?;
assert!(!thumb_bytes.is_empty());
Ok(())
}
Warning: ExifTool creates a backup file named {filename}_original when writing.
use exiftool::{ExifTool, ExifToolError};
use std::path::{Path, PathBuf};
use std::fs;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut exiftool = ExifTool::new()?;
let source_path = Path::new("data/image.jpg");
let new_comment = "Written by exiftool-rs test!";
println!("Writing UserComment to: {}", source_path.display());
exiftool.write_tag(&source_path, "UserComment", new_comment, &[])?;
println!("Write successful (check file metadata externally).");
let read_comment: String = exiftool.read_tag(&source_path, "UserComment")?;
assert_eq!(read_comment, new_comment);
println!("Verification successful!");
Ok(())
}
Uses a temporary file internally. Also creates {filename}_original as backup.
use exiftool::{ExifTool, ExifToolError};
use std::path::{Path, PathBuf};
use std::fs;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut exiftool = ExifTool::new()?;
let source_path = Path::new("data/image.jpg");
// Create some dummy binary data (e.g., a tiny valid JPEG)
let dummy_thumb = b"\xFF\xD8\xFF\xE0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xFF\xDB\x00C\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\xFF\xC0\x00\x11\x08\x00\x01\x00\x01\x03\x01\x22\x00\x02\x11\x01\x03\x11\x01\xFF\xC4\x00\x15\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xDA\x00\x0C\x03\x01\x00\x02\x11\x03\x11\x00\x3F\x00\xA8\xFF\xD9";
println!("Writing binary ThumbnailImage to: {}", source_path.display());
exiftool.write_tag_binary(&source_path, "ThumbnailImage", &dummy_thumb[..], &[])?;
println!("Binary write successful.");
// Verify (Optional)
let read_thumb = exiftool.read_tag_binary(&source_path, "ThumbnailImage")?;
assert_eq!(read_thumb, dummy_thumb);
println!("Binary verification successful!");
Ok(())
}
For commands not covered by helpers, use execute_lines (string lines), json_execute (json value), or execute_raw (
bytes).
use exiftool::{ExifTool, ExifToolError};
fn main() -> Result<(), ExifToolError> {
let mut exiftool = ExifTool::new()?;
let path = "data/image.jpg";
// Example: Get verbose, structured output (-S) as lines
let args = &["-S", "-Make", "-Model", path];
let output_lines = exiftool.execute_lines(args)?;
println!("execute_lines Output:");
for line in output_lines {
println!("> {}", line);
}
// Output:
// > Make: Huawei
// > Model: Nexus 6P
Ok(())
}
ExifData)This crate provides exiftool::ExifData. This struct maps many common fields
output by exiftool -g2 -json. It's useful for accessing typed data for standard image and video metadata.
"-g2" when calling read_metadata.All potentially failing operations return Result<_, ExifToolError>. The
ExifToolError enum covers
various issues, including:
By keeping a single exiftool process running (-stay_open True -@ -), this wrapper avoids the significant startup cost associated with launching exiftool for every command, making it suitable for batch processing or applications requiring frequent metadata access.