1111#import " ReactNativeBlobUtilFS.h"
1212#import " ReactNativeBlobUtilConst.h"
1313#import " ReactNativeBlobUtilFileTransformer.h"
14- #import < AssetsLibrary/AssetsLibrary .h>
14+ #import < Photos/Photos .h>
1515
1616#import < CommonCrypto/CommonDigest.h>
1717
@@ -154,7 +154,7 @@ + (void) readStream:(NSString *)uri
154154 streamId : (NSString *)streamId
155155 baseModule : (ReactNativeBlobUtil*)baseModule
156156{
157- [[self class ] getPathFromUri: uri completionHandler: ^(NSString *path, ALAssetRepresentation *asset) {
157+ [[self class ] getPathFromUri: uri completionHandler: ^(NSString *path, PHAsset *asset) {
158158
159159 __block int read = 0 ;
160160 __block int backoff = tick *1000 ;
@@ -188,17 +188,34 @@ + (void) readStream:(NSString *)uri
188188 }
189189 else if (asset != nil )
190190 {
191- int cursor = 0 ;
192- NSError * err;
193- while ((read = [asset getBytes: buffer fromOffset: cursor length: bufferSize error: &err]) > 0 )
194- {
195- cursor += read;
196- [[self class ] emitDataChunks: [NSData dataWithBytes: buffer length: read] encoding: encoding streamId: streamId baseModule: baseModule];
197- if (tick > 0 )
198- {
199- usleep (backoff);
200- }
201- }
191+ // For PHAsset, we need to get the full image data first
192+ PHImageRequestOptions *options = [[PHImageRequestOptions alloc ] init ];
193+ options.synchronous = YES ;
194+ options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
195+ options.resizeMode = PHImageRequestOptionsResizeModeNone;
196+
197+ [[PHImageManager defaultManager ] requestImageDataForAsset: asset options: options resultHandler: ^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
198+ if (imageData) {
199+ // Write to temporary file and then read it back in chunks
200+ NSString *tempPath = [NSTemporaryDirectory () stringByAppendingPathComponent: [NSString stringWithFormat: @" temp_asset_%@ .tmp" , [[NSUUID UUID ] UUIDString ]]];
201+ [imageData writeToFile: tempPath atomically: YES ];
202+
203+ NSInputStream *stream = [[NSInputStream alloc ] initWithFileAtPath: tempPath];
204+ [stream open ];
205+ while ((read = [stream read: buffer maxLength: bufferSize]) > 0 )
206+ {
207+ [[self class ] emitDataChunks: [NSData dataWithBytes: buffer length: read] encoding: encoding streamId: streamId baseModule: baseModule];
208+ if (tick > 0 )
209+ {
210+ usleep (backoff);
211+ }
212+ }
213+ [stream close ];
214+
215+ // Clean up temp file
216+ [[NSFileManager defaultManager ] removeItemAtPath: tempPath error: nil ];
217+ }
218+ }];
202219 }
203220 else
204221 {
@@ -290,7 +307,7 @@ + (void) emitDataChunks:(NSData *)data encoding:(NSString *) encoding streamId:(
290307
291308+ (NSNumber *) writeFileFromFile : (NSString *)src toFile : (NSString *)dest append : (BOOL )append callback : (void (^)(NSString * errMsg, NSNumber *size))callback
292309{
293- [[self class ] getPathFromUri: src completionHandler: ^(NSString *path, ALAssetRepresentation *asset) {
310+ [[self class ] getPathFromUri: src completionHandler: ^(NSString *path, PHAsset *asset) {
294311 if (path != nil )
295312 {
296313 __block NSInputStream * is = [[NSInputStream alloc ] initWithFileAtPath: path];
@@ -313,21 +330,24 @@ + (NSNumber *) writeFileFromFile:(NSString *)src toFile:(NSString *)dest append:
313330 }
314331 else if (asset != nil )
315332 {
316-
317- __block NSOutputStream * os = [[NSOutputStream alloc ] initToFileAtPath: dest append: append];
318- int read = 0 ;
319- int cursor = 0 ;
320- __block long written = 0 ;
321- uint8_t buffer[10240 ];
322- [os open ];
323- while ((read = [asset getBytes: buffer fromOffset: cursor length: 10240 error: nil ]) > 0 )
324- {
325- cursor += read;
326- [os write: buffer maxLength: read];
327- }
328- __block NSNumber * size = [NSNumber numberWithLong: written];
329- [os close ];
330- callback (nil , size);
333+ // For PHAsset, get the full image data and write it
334+ PHImageRequestOptions *options = [[PHImageRequestOptions alloc ] init ];
335+ options.synchronous = YES ;
336+ options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
337+ options.resizeMode = PHImageRequestOptionsResizeModeNone;
338+
339+ [[PHImageManager defaultManager ] requestImageDataForAsset: asset options: options resultHandler: ^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
340+ if (imageData) {
341+ NSOutputStream *os = [[NSOutputStream alloc ] initToFileAtPath: dest append: append];
342+ [os open ];
343+ [imageData writeToFile: dest atomically: YES ];
344+ NSNumber *size = [NSNumber numberWithLong: imageData.length];
345+ [os close ];
346+ callback (nil , size);
347+ } else {
348+ callback (@" Failed to get image data from asset" , nil );
349+ }
350+ }];
331351 }
332352 else
333353 callback (@" failed to resolve path" , nil );
@@ -496,23 +516,26 @@ + (void) readFile:(NSString *)path
496516 transformFile : (BOOL ) transformFile
497517 onComplete : (void (^)(NSData * content, NSString * codeStr, NSString * errMsg))onComplete
498518{
499- [[self class ] getPathFromUri: path completionHandler: ^(NSString *path, ALAssetRepresentation *asset) {
519+ [[self class ] getPathFromUri: path completionHandler: ^(NSString *path, PHAsset *asset) {
500520 __block NSData * fileContent;
501521 NSError * err;
502522 __block Byte * buffer;
503523 if (asset != nil )
504524 {
505- int size = asset.size ;
506- buffer = (Byte *)malloc (size);
507- [asset getBytes: buffer fromOffset: 0 length: asset.size error: &err];
508- if (err != nil )
509- {
510- onComplete (nil , @" EUNSPECIFIED" , [err description ]);
511- free (buffer);
512- return ;
513- }
514- fileContent = [NSData dataWithBytes: buffer length: size];
515- free (buffer);
525+ // For PHAsset, get the full image data
526+ PHImageRequestOptions *options = [[PHImageRequestOptions alloc ] init ];
527+ options.synchronous = YES ;
528+ options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
529+ options.resizeMode = PHImageRequestOptionsResizeModeNone;
530+
531+ [[PHImageManager defaultManager ] requestImageDataForAsset: asset options: options resultHandler: ^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
532+ if (imageData) {
533+ onComplete (imageData, nil , nil );
534+ } else {
535+ onComplete (nil , @" EUNSPECIFIED" , @" Failed to get image data from asset" );
536+ }
537+ }];
538+ return ;
516539 }
517540 else
518541 {
@@ -795,7 +818,7 @@ + (NSDictionary *) stat:(NSString *) path error:(NSError **) error {
795818
796819+ (void ) exists : (NSString *) path callback : (RCTResponseSenderBlock)callback
797820{
798- [[self class ] getPathFromUri: path completionHandler: ^(NSString *path, ALAssetRepresentation *asset) {
821+ [[self class ] getPathFromUri: path completionHandler: ^(NSString *path, PHAsset *asset) {
799822 if (path != nil )
800823 {
801824 BOOL isDir = NO ;
@@ -886,7 +909,7 @@ + (void)slice:(NSString *)path
886909 resolver : (RCTPromiseResolveBlock)resolve
887910 rejecter : (RCTPromiseRejectBlock)reject
888911{
889- [[self class ] getPathFromUri: path completionHandler: ^(NSString *path, ALAssetRepresentation *asset)
912+ [[self class ] getPathFromUri: path completionHandler: ^(NSString *path, PHAsset *asset)
890913 {
891914 if (path != nil )
892915 {
@@ -945,39 +968,34 @@ + (void)slice:(NSString *)path
945968 }
946969 else if (asset != nil )
947970 {
948- long expected = [end longValue ] - [start longValue ];
949- long read = 0 ;
950- long chunkRead = 0 ;
951- NSOutputStream * os = [[NSOutputStream alloc ] initToFileAtPath: dest append: NO ];
952- [os open ];
953- long size = asset.size ;
954- long max = MIN (size, [end longValue ]);
955-
956- while (read < expected) {
957- uint8_t chunk[10240 ];
958- uint8_t * pointerToChunk = &chunk[0 ];
959- long chunkSize = 0 ;
960- if ([start longValue ] + read + 10240 > max)
961- {
962- NSLog (@" read chunk %lu " , max - read - [start longValue ]);
963- chunkSize = max - read - [start longValue ];
964- chunkRead = [asset getBytes: pointerToChunk fromOffset: [start longValue ] + read length: chunkSize error: nil ];
965- }
966- else
967- {
968- NSLog (@" read chunk %lu " , 10240 );
969- chunkSize = 10240 ;
970- chunkRead = [asset getBytes: pointerToChunk fromOffset: [start longValue ] + read length: chunkSize error: nil ];
971+ // For PHAsset, get the full image data and then slice it
972+ PHImageRequestOptions *options = [[PHImageRequestOptions alloc ] init ];
973+ options.synchronous = YES ;
974+ options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
975+ options.resizeMode = PHImageRequestOptionsResizeModeNone;
976+
977+ [[PHImageManager defaultManager ] requestImageDataForAsset: asset options: options resultHandler: ^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
978+ if (imageData) {
979+ long startPos = [start longValue ];
980+ long endPos = [end longValue ];
981+ long dataLength = imageData.length ;
982+
983+ // Clamp the range to the actual data length
984+ startPos = MAX (0 , MIN (startPos, dataLength));
985+ endPos = MAX (startPos, MIN (endPos, dataLength));
986+
987+ NSRange range = NSMakeRange (startPos, endPos - startPos);
988+ NSData *sliceData = [imageData subdataWithRange: range];
989+
990+ if ([sliceData writeToFile: dest atomically: YES ]) {
991+ resolve (dest);
992+ } else {
993+ reject (@" EUNSPECIFIED" , @" Failed to write slice to file" , nil );
994+ }
995+ } else {
996+ reject (@" EUNSPECIFIED" , @" Failed to get image data from asset" , nil );
971997 }
972- if ( chunkRead <= 0 )
973- break ;
974- long remain = expected - read;
975-
976- [os write: chunk maxLength: chunkSize];
977- read += chunkRead;
978- }
979- [os close ];
980- resolve (dest);
998+ }];
981999 }
9821000 else {
9831001 reject (@" EINVAL" , [NSString stringWithFormat: @" Could not resolve URI %@ " , path ], nil );
@@ -1001,20 +1019,18 @@ - (void)closeInStream
10011019
10021020# pragma mark - get absolute path of resource
10031021
1004- + (void ) getPathFromUri : (NSString *)uri completionHandler : (void (^)(NSString * path, ALAssetRepresentation *asset)) onComplete
1022+ + (void ) getPathFromUri : (NSString *)uri completionHandler : (void (^)(NSString * path, PHAsset *asset)) onComplete
10051023{
10061024 if ([uri hasPrefix: AL_PREFIX])
10071025 {
10081026 NSURL *asseturl = [NSURL URLWithString: uri];
1009- __block ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc ] init ];
1010- [assetslibrary assetForURL: asseturl
1011- resultBlock: ^(ALAsset *asset) {
1012- __block ALAssetRepresentation * present = [asset defaultRepresentation ];
1013- onComplete (nil , present);
1014- }
1015- failureBlock: ^(NSError *error) {
1016- onComplete (nil , nil );
1017- }];
1027+ PHFetchResult<PHAsset *> *assets = [PHAsset fetchAssetsWithALAssetURLs: @[asseturl] options: nil ];
1028+ if (assets.count > 0 ) {
1029+ PHAsset *asset = assets.firstObject ;
1030+ onComplete (nil , asset);
1031+ } else {
1032+ onComplete (nil , nil );
1033+ }
10181034 }
10191035 else
10201036 {
@@ -1044,20 +1060,20 @@ +(void) df:(RCTResponseSenderBlock)callback
10441060
10451061}
10461062
1047- + (void ) writeAssetToPath : (ALAssetRepresentation * )rep dest : (NSString *)dest
1063+ + (void ) writeAssetToPath : (PHAsset * )asset dest : (NSString *)dest
10481064{
1049- int read = 0 ;
1050- int cursor = 0 ;
1051- Byte * buffer = (Byte *)malloc (10240 );
1052- NSOutputStream * ostream = [[NSOutputStream alloc ] initToFileAtPath: dest append: NO ];
1053- [ostream open ];
1054- while ((read = [rep getBytes: buffer fromOffset: cursor length: 10240 error: nil ]) > 0 )
1055- {
1056- cursor+=10240 ;
1057- [ostream write: buffer maxLength: read];
1065+ if (asset.mediaType == PHAssetMediaTypeImage) {
1066+ PHImageRequestOptions *options = [[PHImageRequestOptions alloc ] init ];
1067+ options.synchronous = YES ;
1068+ options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
1069+ options.resizeMode = PHImageRequestOptionsResizeModeNone;
1070+
1071+ [[PHImageManager defaultManager ] requestImageDataForAsset: asset options: options resultHandler: ^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
1072+ if (imageData) {
1073+ [imageData writeToFile: dest atomically: YES ];
1074+ }
1075+ }];
10581076 }
1059- [ostream close ];
1060- free (buffer);
10611077 return ;
10621078}
10631079
0 commit comments