expo-media-metadata
v1.0.1
Published
Native Expo module for reading media metadata — EXIF, GPS, IPTC, C2PA, checksums and more. Reads directly from original file bytes (supports ph:// on iOS) without losing data during cache copies.
Maintainers
Readme
expo-media-metadata
A native Expo module for reading rich media metadata from images and videos — including EXIF, GPS, TIFF, IPTC, XMP, PNG, C2PA/JUMBF, file checksums, and more.
Unlike JavaScript-based solutions, this module reads directly from the original file bytes on the native layer:
- On iOS, supports
ph://URIs so you can read the original PHAsset bytes from the gallery — bypassing the re-encoding thatexpo-image-pickerperforms, which would otherwise strip C2PA provenance data. - On Android, reads directly via
content://andfile://URIs using the metadata-extractor library.
Platform Support
| Platform | Minimum Version | Notes |
|---|---|---|
| iOS | 16.0+ | Supports file://, ph:// (PHAsset) URIs |
| Android | API 24+ | Supports content://, file:// URIs |
Installation
npx expo install expo-media-metadataiOS (CocoaPods)
cd ios && pod installAndroid
No extra steps needed — the Gradle plugin handles it automatically.
API
getMediaMetadata(uri: string): Promise<MediaMetadataResult>
Reads all available metadata from a media file.
Parameters
uri— File URI of the media. Accepts:file:///...— Direct file pathph://<assetId>— PHAsset URI (iOS only — reads original, unmodified bytes)content://...— Android content URI
Returns a MediaMetadataResult object with structured metadata fields.
import { getMediaMetadata } from 'expo-media-metadata';
const metadata = await getMediaMetadata('file:///path/to/image.jpg');
console.log(metadata.exif); // EXIF key-value pairs
console.log(metadata.metadata); // All metadata (GPS, IPTC, C2PA, checksums, etc.)getMediaRawBase64(uri: string): Promise<string>
Reads the raw bytes of a media file and returns a base64-encoded string. Useful for C2PA verification where the complete binary is needed.
import { getMediaRawBase64 } from 'expo-media-metadata';
const base64 = await getMediaRawBase64('file:///path/to/image.jpg');Types
type MediaMetadataResult = {
/** EXIF metadata as key-value pairs */
exif: Record<string, any> | null;
/**
* All metadata including TIFF, GPS, IPTC, XMP, PNG, C2PA/JUMBF,
* file checksums (MD5, SHA256), file size, MIME type, etc.
*/
metadata: Record<string, any> | null;
};Example metadata keys
| Key | Description |
|---|---|
| file_name | Original filename |
| file_size | File size in bytes |
| mime_type | e.g. "image/jpeg" |
| file_type_extension | e.g. "jpg" |
| checksum_md5 | MD5 hex digest of file bytes |
| checksum_sha256 | SHA-256 hex digest of file bytes |
| Exif.* | All EXIF fields (e.g. Exif.DateTimeOriginal) |
| GPS.* | GPS fields (e.g. GPS.Latitude, GPS.Longitude) |
| TIFF.* | TIFF fields (e.g. TIFF.Make, TIFF.Model) |
| IPTC.* | IPTC fields |
| c2pa:has_c2pa | true if C2PA manifest is present |
| c2pa:c2pa_claim_found | true if a C2PA claim box was found |
| c2pa:digital_source_type | e.g. "trainedAlgorithmicMedia" |
Real-World Usage Example
[!WARNING]
Metadata Loss Warning: When using libraries likeexpo-image-picker, do NOT lower the image quality (e.g., avoidquality: 0.8) or enable cropping/editing (allowsEditing: true). Doing so forces the OS to re-encode the file, which irreversibly strips out rich metadata like C2PA, GPS, and sensitive EXIF. Always request the unmodified image.
This example shows how to use expo-media-metadata with expo-image-picker to extract metadata from a picked image, including reading original PHAsset bytes on iOS (preserving C2PA provenance data):
import { launchImageLibraryAsync } from 'expo-image-picker';
import { getMediaMetadata } from 'expo-media-metadata';
import { Platform } from 'react-native';
async function pickAndReadMetadata() {
const result = await launchImageLibraryAsync({
mediaTypes: ['images'],
exif: true,
quality: 1,
});
if (result.canceled) return;
const asset = result.assets[0];
// On iOS, use ph://<assetId> to read the original PHAsset bytes
// (bypasses re-encoding that strips C2PA data)
const metadataUri =
Platform.OS === 'ios' && asset.assetId
? `ph://${asset.assetId}`
: asset.uri;
const metadata = await getMediaMetadata(metadataUri);
console.log('EXIF:', metadata.exif);
console.log('All metadata:', metadata.metadata);
// Check for C2PA content credentials
if (metadata.metadata?.['c2pa:has_c2pa']) {
console.log('✅ Image has C2PA provenance data');
console.log('Source type:', metadata.metadata['c2pa:digital_source_type']);
}
}What metadata is extracted?
iOS (via ImageIO framework)
- Full EXIF dictionary (camera settings, timestamps, etc.)
- GPS coordinates and altitude
- TIFF fields (Make, Model, Software, etc.)
- IPTC (caption, keywords, copyright)
- XMP metadata
- PNG metadata
- HEIC/HEIF properties
- C2PA / JUMBF manifest detection (APP11 in JPEG, caBX chunk in PNG)
- File-level: size, MD5 checksum, SHA-256 checksum, MIME type
Android (via metadata-extractor library)
- EXIF, GPS, IPTC, XMP
- JPEG, PNG, WebP container metadata
- File-level: size, MIME type
Why this module?
| Approach | EXIF | C2PA | Original bytes | ph:// support |
|---|---|---|---|---|
| expo-image-picker (built-in exif: true) | ⚠️ Partial | ❌ Lost on copy | ❌ | ❌ |
| JavaScript EXIF parsers | ✅ | ❌ | Depends | ❌ |
| expo-media-metadata | ✅ | ✅ | ✅ | ✅ (iOS) |
License
MIT © Arun Kumar Bhardwaj
