Skip to content

Commit

Permalink
feat: add function for get image metadata exif
Browse files Browse the repository at this point in the history
  • Loading branch information
numandev1 committed Nov 15, 2023
1 parent 31fe1c8 commit 779ab91
Show file tree
Hide file tree
Showing 13 changed files with 151 additions and 9 deletions.
32 changes: 30 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ We should use **react-native-compressor** instead of **FFmpeg** because **react-
* [Other Utilities](#api)
- [Background Upload](#background-upload-1)
- [Cancel Background Upload](#cancel-background-upload-1)
- [Get Metadata Of Image](#get-metadata-of-image)
- [Get Metadata Of Video](#get-metadata-of-video)
- [Get Real Path](#get-real-path)
- [Get Temp file Path](#get-temp-file-path)
Expand Down Expand Up @@ -438,6 +439,8 @@ await clearCache(); // this will clear cache of thumbnails cache directory
- ###### `returnableOutputType: ReturnableOutputType` (default: uri)
Can be either `uri` or `base64`, defines the Returnable output image format.
**if you wanna get image metadata (exif) then [read this](#get-metadata-of-image)**
## Video
- ###### `compress(url: string, options?: videoCompresssionType , onProgress?: (progress: number)): Promise<string>`
Expand All @@ -451,6 +454,9 @@ await clearCache(); // this will clear cache of thumbnails cache directory
- ###### `deactivateBackgroundTask(): Promise<any>`
if you call `activateBackgroundTask` method, then after video compression, you should call `deactivateBackgroundTask` for disable background task mode.
- ###### `getCancellationId: function`
`getCancellationId` is a callback function that gives us compress video id, which can be used in `Video.cancelCompression` method to cancel the compression

### videoCompresssionType

- ###### `compressionMethod: compressionMethod` (default: "manual")
Expand All @@ -477,8 +483,7 @@ await clearCache(); // this will clear cache of thumbnails cache directory

previously default was 16 but now it is 0 by default. 0 mean 0mb. This is an offset, which you can set for minimumFileSizeForCompress will allow this package to dont compress less than or equal to `minimumFileSizeForCompress` ref [#26](https://github.com/numandev1/react-native-compressor/issues/26)

- ###### `getCancellationId: function`
`getCancellationId` is a callback function that gives us compress video id, which can be used in `Video.cancelCompression` method to cancel the compression
**if you wanna get video metadata then [read this](#get-metadata-of-video)**

## Audio

Expand Down Expand Up @@ -597,6 +602,29 @@ const metaData = await getVideoMetaData(filePath);

- ###### `getVideoMetaData(path: string)`

### Get Metadata Of Image

if you want to get metadata of video than you can use this function

```js
import { getImageMetaData } from 'react-native-compressor';

const metaData = await getImageMetaData(filePath);
```

```
{
"ImageWidth": 4032,
"ImageHeight": 3024,
"Orientation": 3,
"size": 4127057,
"extension": "jpg",
"exif":{...}
}
```

- ###### `getImageMetaData(path: string)`

### Get Real Path

if you want to convert
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import com.reactnativecompressor.Utils.Uploader
import com.reactnativecompressor.Utils.Utils
import com.reactnativecompressor.Utils.Utils.generateCacheFilePath
import com.reactnativecompressor.Utils.Utils.getRealPath
import com.reactnativecompressor.Utils.convertReadableMapToUploaderOptions
import com.reactnativecompressor.Video.VideoMain

class CompressorModule(private val reactContext: ReactApplicationContext) : CompressorSpec(reactContext) {
Expand Down Expand Up @@ -47,6 +46,11 @@ class CompressorModule(private val reactContext: ReactApplicationContext) : Comp
imageMain.image_compress(imagePath,optionMap,promise)
}

@ReactMethod
override fun getImageMetaData(filePath: String, promise: Promise) {
imageMain.getImageMetaData(filePath,promise)
}

// VIdeo
@ReactMethod
override fun compress(
Expand Down
32 changes: 32 additions & 0 deletions android/src/main/java/com/reactnativecompressor/Image/ImageMain.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package com.reactnativecompressor.Image

import android.media.ExifInterface
import android.net.Uri
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReadableMap
import com.reactnativecompressor.Utils.MediaCache
import com.reactnativecompressor.Utils.Utils
import com.reactnativecompressor.Utils.Utils.exifAttributes
import java.io.File

class ImageMain(private val reactContext: ReactApplicationContext) {
fun image_compress(
Expand All @@ -28,4 +33,31 @@ class ImageMain(private val reactContext: ReactApplicationContext) {
promise.reject(ex)
}
}

fun getImageMetaData(filePath: String, promise: Promise) {
var filePath: String? = filePath
try {
filePath = Utils.getRealPath(filePath, reactContext)
val uri = Uri.parse(filePath)
val srcPath = uri.path
val params = Arguments.createMap()
val file = File(srcPath)
val sizeInKBs = (file.length() / 1024).toDouble()
val exif = ExifInterface(srcPath!!)
for (tag in exifAttributes) {
val value: String? = exif.getAttribute(tag)
if(value != null)
{
params.putString(tag, value)
}

}
val extension = filePath!!.substring(filePath.lastIndexOf(".") + 1)
params.putDouble("size", sizeInKBs)
params.putString("extension", extension)
promise.resolve(params)
} catch (e: Exception) {
promise.reject(e)
}
}
}
1 change: 1 addition & 0 deletions android/src/oldarch/CompressorSpec.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ abstract class CompressorSpec(context: ReactApplicationContext?) : ReactContextB
abstract fun generateFilePath(_extension: String, promise: Promise)
abstract fun getRealPath(path: String, type: String, promise: Promise)
abstract fun getVideoMetaData(filePath: String, promise: Promise)
abstract fun getImageMetaData(String filePath, Promise promise);
abstract fun getFileSize(filePath: String, promise: Promise)
abstract fun compress(fileUrl: String, optionMap: ReadableMap, promise: Promise)
abstract fun cancelCompression(uuid: String)
Expand Down
4 changes: 2 additions & 2 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -880,7 +880,7 @@ PODS:
- React-Codegen
- React-RCTFabric
- ReactCommon/turbomodule/core
- react-native-compressor (1.8.17):
- react-native-compressor (1.8.19):
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- RCTRequired
Expand Down Expand Up @@ -1377,7 +1377,7 @@ SPEC CHECKSUMS:
React-jsinspector: 194e32c6aab382d88713ad3dd0025c5f5c4ee072
React-logger: cebf22b6cf43434e471dc561e5911b40ac01d289
react-native-cameraroll: 5d9523136a929b58f092fd7f0a9a13367a4b46e3
react-native-compressor: 8b6e302c4531f93aeaabd9658495bf9855adf3bd
react-native-compressor: 6b7e979126569ab20a5a53e17d569158463064e1
react-native-document-picker: c9ac93d7b511413f4a0ed61c92ff6c7b1bcf4f94
react-native-get-random-values: dee677497c6a740b71e5612e8dbd83e7539ed5bb
react-native-image-picker: 9b4b1d0096500050cbdabf8f4fd00b771065d983
Expand Down
7 changes: 5 additions & 2 deletions example/src/Screens/Image/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import type { ProgressBarRafType } from '../../Components/ProgressBar';
import * as ImagePicker from 'react-native-image-picker';
import CameraRoll from '@react-native-camera-roll/camera-roll';
import prettyBytes from 'pretty-bytes';
import { Image, getFileSize } from 'react-native-compressor';
import { Image, getFileSize, getImageMetaData } from 'react-native-compressor';
import { getFileInfo } from '../../Utils';
const Index = () => {
const progressRef = useRef<ProgressBarRafType>();
Expand All @@ -26,7 +26,7 @@ const Index = () => {
const [orignalSize, setOrignalSize] = useState<string>('');
const [compressedSize, setCompressedSize] = useState(0);

const compressHandler = (result: ImagePicker.ImagePickerResponse) => {
const compressHandler = async (result: ImagePicker.ImagePickerResponse) => {
if (result.didCancel) {
Alert.alert('Failed selecting Image');
return;
Expand All @@ -40,9 +40,12 @@ const Index = () => {
setMimeType(source.type);
setOrignalUri(source.uri);
}
const metadata = await getImageMetaData(source.uri);
console.log(JSON.stringify(metadata, null, 4), 'metadata');
Image.compress(source.uri)
.then(async (compressedFileUri) => {
console.log(compressedFileUri, 'compressedFileUri');

setCommpressedUri(compressedFileUri);
const detail: any = await getFileInfo(compressedFileUri);
setCompressedSize(prettyBytes(parseInt(detail.size || 0)));

Check warning on line 51 in example/src/Screens/Image/index.tsx

View workflow job for this annotation

GitHub Actions / Lint JS (eslint, prettier)

Missing radix parameter.
Expand Down
4 changes: 4 additions & 0 deletions ios/Compressor.mm
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ @interface RCT_EXTERN_MODULE(Compressor, RCTEventEmitter)
withResolver: (RCTPromiseResolveBlock) resolve
withRejecter: (RCTPromiseRejectBlock) reject)

RCT_EXTERN_METHOD(getImageMetaData: (NSString*) filePath
withResolver: (RCTPromiseResolveBlock) resolve
withRejecter: (RCTPromiseRejectBlock) reject)

RCT_EXTERN_METHOD(compress_audio: (NSString*) filePath
withOptions: (NSDictionary*) optionsDict
withResolver: (RCTPromiseResolveBlock) resolve
Expand Down
5 changes: 5 additions & 0 deletions ios/CompressorManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ class Compressor: RCTEventEmitter {
ImageMain.image_compress(imagePath, optionMap: optionMap, resolve: resolve, reject: reject)
}

@objc(getImageMetaData:withResolver:withRejecter:)
func getImageMetaData(_ filePath: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
ImageMain.getImageMetaData(filePath,resolve: resolve,reject: reject)
}

@objc(compress_audio:withOptions:withResolver:withRejecter:)
func compress_audio(_ fileUrl: String, optionMap: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
AudioMain.compress_audio(fileUrl, optionMap: optionMap, resolve: resolve, reject: reject)
Expand Down
24 changes: 24 additions & 0 deletions ios/Image/ImageMain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,28 @@ class ImageMain {
reject(error.localizedDescription, error.localizedDescription, nil)
}
}

static func getImageMetaData(_ filePath: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
let options = ImageCompressorOptions.fromDictionary([:])
ImageCompressor.getAbsoluteImagePath(filePath, options: options) { ImagePath in
if ImagePath.hasPrefix("file://") {
let absoluteImagePath = URL(string: ImagePath)!.path
Utils.getFileSize(absoluteImagePath) { fileSize in
let _extension = (absoluteImagePath as NSString).pathExtension
let imageData = NSData(contentsOfFile: absoluteImagePath)!
let imageSource = CGImageSourceCreateWithData(imageData, nil)!
let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil)!
let exif=imageProperties as NSDictionary
let mutableExif = exif.mutableCopy() as! NSMutableDictionary
if let fileSizeInt = Int(fileSize as! String) {
mutableExif.setValue(fileSizeInt, forKey: "size")
}
mutableExif.setValue(_extension, forKey: "extension")
resolve(mutableExif)
} reject: { code, message, error in
reject(code, message, error)
}
}
}
}
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@
"thumbnail",
"create-thumbnail",
"video-metadata",
"media"
"media",
"bitrate"
],
"repository": "https://github.com/numandev1/react-native-compressor",
"author": "numandev1 <muhammadnuman70@gmail.com> (https://github.com/numandev1)",
Expand Down
1 change: 1 addition & 0 deletions src/Spec/NativeCompressor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { TurboModuleRegistry } from 'react-native';
export interface Spec extends TurboModule {
// Image
image_compress(imagePath: string, optionMap: Object): Promise<string>;
getImageMetaData(filePath: string): Promise<string>;
// Video
compress(fileUrl: string, optionMap: Object): Promise<string>;
cancelCompression(uuid: string): void;
Expand Down
3 changes: 3 additions & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
generateFilePath,
getRealPath,
getVideoMetaData,
getImageMetaData,
getFileSize,
backgroundUpload,
cancelUpload,
Expand All @@ -31,6 +32,7 @@ export {
generateFilePath,
getRealPath,
getVideoMetaData,
getImageMetaData,
createVideoThumbnail,
clearCache,
getFileSize,
Expand All @@ -47,6 +49,7 @@ export default {
generateFilePath,
getRealPath,
getVideoMetaData,
getImageMetaData,
getFileSize,
backgroundUpload,
createVideoThumbnail,
Expand Down
38 changes: 37 additions & 1 deletion src/utils/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable no-bitwise */
import { Compressor } from '../Main';

import { Platform } from 'react-native';
type qualityType = 'low' | 'medium' | 'high';
const INCORRECT_INPUT_PATH = 'Incorrect input path. Please provide a valid one';

Expand Down Expand Up @@ -40,6 +40,16 @@ type createVideoThumbnailType = (
}>;

type clearCacheType = (cacheDir?: string) => Promise<string>;

type getImageMetaDataType = (filePath: string) => Promise<{
ImageWidth: number;
ImageHeight: number;
Orientation: number;
size: number;
extension: string;
exif: { [key: string]: string };
}>;

type getVideoMetaDataType = (filePath: string) => Promise<{
extension: string;
size: number;
Expand Down Expand Up @@ -68,6 +78,32 @@ export const getVideoMetaData: getVideoMetaDataType = (path: string) => {
return Compressor.getVideoMetaData(path);
};

const unifyMetaData = (exifResult: any) => {
const output: any = {};
const isIos = Platform.OS === 'ios';
output.ImageWidth = isIos
? exifResult?.PixelWidth
: parseInt(exifResult.ImageWidth);

output.ImageHeight = isIos
? exifResult?.PixelHeight
: parseInt(exifResult.ImageLength);

output.Orientation = isIos
? exifResult.Orientation
: parseInt(exifResult.Orientation);

output.size = exifResult.size;
output.extension = exifResult.extension;
output.exif = exifResult;
return output;
};

export const getImageMetaData: getImageMetaDataType = async (path: string) => {
const result = await Compressor.getImageMetaData(path);
return unifyMetaData(result);
};

export const createVideoThumbnail: createVideoThumbnailType = (
fileUrl,
options = {}
Expand Down

0 comments on commit 779ab91

Please sign in to comment.