Skip to content

Commit bd12e41

Browse files
wood1986facebook-github-bot
authored andcommitted
fix: fix the race condition when calling readAsDataURL after new Blob(blobs) (#34096)
Summary: ```js async () => { let blobs = []; for (let i = 0; i < 4; i++) { const res = await fetch(); blobs = [...blobs, await res.blob()] } const blob = new Blob(blobs); // <<<<<<<<<<<<<<< a return await new Promise((resolve, reject) => { const fileReader = new FileReader(); fileReader.onload = async () => { await RNFS.writeFile(destPath, (fileReader.result as string).split(',')[1], 'base64'); resolve(destPath); }; fileReader.onabort = () => { reject(''); }; fileReader.onerror = (event) => { reject(''); }; fileReader.readAsDataURL(blob); // <<<<<<<<<<<<<<< b }); } ``` Sometime `fileReader.readAsDataURL` is unable to get blob from the dictionary after `new Blob(blobs)` and then reject with `Unable to resolve data for blob: blobId` in iOS or `The specified blob is invalid` in android. Because line `a` and `b` can be run in different thread. `new Blob([])` is in progress and `fileReader.readAsDataURL` accesses the blob dictionary ahead of the blob creation. The expected behaviour is it should finish new Blob([]) first and then readAsDataURL(blob) To fix that, there should be a lock inside the method `createFromParts`. For iOS, It needs to be a recursive_mutex to allow same thread to acquire lock ## Changelog <!-- Help reviewers and the release process by writing your own changelog entry. For an example, see: https://github.com/facebook/react-native/wiki/Changelog --> [iOS] [Fixed] - fix the race condition when calling readAsDataURL after new Blob(blobs) Pull Request resolved: #34096 Reviewed By: cipolleschi Differential Revision: D37514981 Pulled By: javache fbshipit-source-id: 4bf84ece99871276ecaa5aa1849b9145ff44dbf4
1 parent d6c08bd commit bd12e41

File tree

1 file changed

+31
-27
lines changed

1 file changed

+31
-27
lines changed

Libraries/Blob/RCTFileReaderModule.mm

+31-27
Original file line numberDiff line numberDiff line change
@@ -31,44 +31,48 @@ @implementation RCTFileReaderModule
3131
reject:(RCTPromiseRejectBlock)reject)
3232
{
3333
RCTBlobManager *blobManager = [_moduleRegistry moduleForName:"BlobModule"];
34-
NSData *data = [blobManager resolve:blob];
34+
dispatch_async(blobManager.methodQueue, ^{
35+
NSData *data = [blobManager resolve:blob];
3536

36-
if (data == nil) {
37-
reject(RCTErrorUnspecified,
38-
[NSString stringWithFormat:@"Unable to resolve data for blob: %@", [RCTConvert NSString:blob[@"blobId"]]], nil);
39-
} else {
40-
NSStringEncoding stringEncoding;
41-
42-
if (encoding == nil) {
43-
stringEncoding = NSUTF8StringEncoding;
37+
if (data == nil) {
38+
reject(RCTErrorUnspecified,
39+
[NSString stringWithFormat:@"Unable to resolve data for blob: %@", [RCTConvert NSString:blob[@"blobId"]]], nil);
4440
} else {
45-
stringEncoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef) encoding));
46-
}
41+
NSStringEncoding stringEncoding;
4742

48-
NSString *text = [[NSString alloc] initWithData:data encoding:stringEncoding];
43+
if (encoding == nil) {
44+
stringEncoding = NSUTF8StringEncoding;
45+
} else {
46+
stringEncoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef) encoding));
47+
}
4948

50-
resolve(text);
51-
}
49+
NSString *text = [[NSString alloc] initWithData:data encoding:stringEncoding];
50+
51+
resolve(text);
52+
}
53+
});
5254
}
5355

5456
RCT_EXPORT_METHOD(readAsDataURL:(NSDictionary<NSString *, id> *)blob
5557
resolve:(RCTPromiseResolveBlock)resolve
5658
reject:(RCTPromiseRejectBlock)reject)
5759
{
5860
RCTBlobManager *blobManager = [_moduleRegistry moduleForName:"BlobModule"];
59-
NSData *data = [blobManager resolve:blob];
60-
61-
if (data == nil) {
62-
reject(RCTErrorUnspecified,
63-
[NSString stringWithFormat:@"Unable to resolve data for blob: %@", [RCTConvert NSString:blob[@"blobId"]]], nil);
64-
} else {
65-
NSString *type = [RCTConvert NSString:blob[@"type"]];
66-
NSString *text = [NSString stringWithFormat:@"data:%@;base64,%@",
67-
type != nil && [type length] > 0 ? type : @"application/octet-stream",
68-
[data base64EncodedStringWithOptions:0]];
69-
70-
resolve(text);
71-
}
61+
dispatch_async(blobManager.methodQueue, ^{
62+
NSData *data = [blobManager resolve:blob];
63+
64+
if (data == nil) {
65+
reject(RCTErrorUnspecified,
66+
[NSString stringWithFormat:@"Unable to resolve data for blob: %@", [RCTConvert NSString:blob[@"blobId"]]], nil);
67+
} else {
68+
NSString *type = [RCTConvert NSString:blob[@"type"]];
69+
NSString *text = [NSString stringWithFormat:@"data:%@;base64,%@",
70+
type != nil && [type length] > 0 ? type : @"application/octet-stream",
71+
[data base64EncodedStringWithOptions:0]];
72+
73+
resolve(text);
74+
}
75+
});
7276
}
7377

7478
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params

0 commit comments

Comments
 (0)