Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add function for get image metadata exif #243

Merged
merged 1 commit into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 * 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 [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,12 +40,15 @@
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.
})
.catch((e) => {
console.log(e, 'error');
Expand Down Expand Up @@ -85,7 +88,7 @@
'https://svs.gsfc.nasa.gov/vis/a030000/a030800/a030877/frames/5760x3240_16x9_01p/BlackMarble_2016_1200m_africa_s_labeled.png';
setFileName('test.jpg');
const size = await getFileSize(url);
setOrignalSize(prettyBytes(parseInt(size)));

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

View workflow job for this annotation

GitHub Actions / Lint JS (eslint, prettier)

Missing radix parameter.
setMimeType('image/jpeg');
Image.compress(url, {
progressDivider: 10,
Expand All @@ -98,7 +101,7 @@
console.log('test', commpressedUri);
setCommpressedUri(compressedFileUri);
const detail: any = await getFileInfo(compressedFileUri);
setCompressedSize(prettyBytes(parseInt(detail.size || 0)));

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

View workflow job for this annotation

GitHub Actions / Lint JS (eslint, prettier)

Missing radix parameter.
})
.catch((e) => {
console.log(e, 'error1');
Expand All @@ -117,7 +120,7 @@
.then(async (compressedFileUri) => {
setCommpressedUri(compressedFileUri);
const detail: any = await getFileInfo(compressedFileUri);
setCompressedSize(prettyBytes(parseInt(detail.size)));

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

View workflow job for this annotation

GitHub Actions / Lint JS (eslint, prettier)

Missing radix parameter.
})
.catch((e) => {
console.log(e, 'error');
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
Loading