mirror of
https://github.com/status-im/react-native.git
synced 2025-01-27 17:54:48 +00:00
Added RCTFileRequestHandler
Summary: @public We previously discovered that using an NSURLSessionDataTask to load local files is noticably less efficient than using regular filesystem methods. This diff adds RCTFileRequestHandler as a replacement for RCTHTTPRequestHandler when loading local files. This reduces loading time when loading local files via XMLHttpRequest, as well as improving the performance for some image load requests. Reviewed By: @javache Differential Revision: D2531710 fb-gh-sync-id: 259714baac131784de494d24939f42ad52bff41a
This commit is contained in:
parent
28f6eba22d
commit
a92f107712
@ -86,7 +86,7 @@ RCT_EXPORT_MODULE()
|
||||
}
|
||||
|
||||
RCTDownloadTask *task = [_bridge.networking downloadTaskWithRequest:request completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) {
|
||||
if (response && !error) {
|
||||
if (response && !error && [response.URL.scheme hasPrefix:@"http"]) {
|
||||
RCTImageDownloader *strongSelf = weakSelf;
|
||||
NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data userInfo:nil storagePolicy:NSURLCacheStorageAllowed];
|
||||
[strongSelf->_cache storeCachedResponse:cachedResponse forRequest:request];
|
||||
@ -106,9 +106,15 @@ RCT_EXPORT_MODULE()
|
||||
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
|
||||
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
|
||||
{
|
||||
if ([imageURL.scheme.lowercaseString hasPrefix:@"http"]) {
|
||||
if ([imageURL.scheme.lowercaseString hasPrefix:@"http"] ||
|
||||
[imageURL.scheme caseInsensitiveCompare:@"file"] == NSOrderedSame) {
|
||||
__block RCTImageLoaderCancellationBlock decodeCancel = nil;
|
||||
|
||||
// Add missing png extension
|
||||
if (imageURL.fileURL && imageURL.pathExtension.length == 0) {
|
||||
imageURL = [NSURL fileURLWithPath:[imageURL.path stringByAppendingPathExtension:@"png"]];
|
||||
}
|
||||
|
||||
__weak RCTImageDownloader *weakSelf = self;
|
||||
RCTImageLoaderCancellationBlock downloadCancel = [self downloadDataForURL:imageURL progressHandler:progressHandler completionHandler:^(NSError *error, NSData *imageData) {
|
||||
if (error) {
|
||||
@ -153,31 +159,6 @@ RCT_EXPORT_MODULE()
|
||||
return ^{
|
||||
cancelled = YES;
|
||||
};
|
||||
} else if ([imageURL.scheme isEqualToString:@"file"]) {
|
||||
__block BOOL cancelled = NO;
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
UIImage *image = [UIImage imageWithContentsOfFile:imageURL.resourceSpecifier];
|
||||
if (image) {
|
||||
if (progressHandler) {
|
||||
progressHandler(1, 1);
|
||||
}
|
||||
if (completionHandler) {
|
||||
completionHandler(nil, image);
|
||||
}
|
||||
} else {
|
||||
if (completionHandler) {
|
||||
NSString *message = [NSString stringWithFormat:@"Could not find image at path: %@", imageURL.absoluteString];
|
||||
completionHandler(RCTErrorWithMessage(message), nil);
|
||||
}
|
||||
}
|
||||
});
|
||||
return ^{
|
||||
cancelled = YES;
|
||||
};
|
||||
} else {
|
||||
RCTLogError(@"Unexpected image schema %@", imageURL.scheme);
|
||||
return ^{};
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
#import "RCTDownloadTask.h"
|
||||
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTLog.h"
|
||||
|
||||
@implementation RCTDownloadTask
|
||||
{
|
||||
@ -65,8 +65,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
||||
{
|
||||
if (![requestToken isEqual:_requestToken]) {
|
||||
if (RCT_DEBUG) {
|
||||
RCTAssert([requestToken isEqual:_requestToken],
|
||||
@"Unrecognized request token: %@", requestToken);
|
||||
RCTLogError(@"Unrecognized request token: %@ expected: %@", requestToken, _requestToken);
|
||||
}
|
||||
if (_completionBlock) {
|
||||
_completionBlock(_response, _data, [NSError errorWithDomain:RCTErrorDomain code:0
|
||||
|
18
Libraries/Network/RCTFileRequestHandler.h
Normal file
18
Libraries/Network/RCTFileRequestHandler.h
Normal file
@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import "RCTURLRequestHandler.h"
|
||||
#import "RCTInvalidating.h"
|
||||
|
||||
/**
|
||||
* This is the default RCTURLRequestHandler implementation for file requests.
|
||||
*/
|
||||
@interface RCTFileRequestHandler : NSObject <RCTURLRequestHandler, RCTInvalidating>
|
||||
|
||||
@end
|
88
Libraries/Network/RCTFileRequestHandler.m
Normal file
88
Libraries/Network/RCTFileRequestHandler.m
Normal file
@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import "RCTFileRequestHandler.h"
|
||||
|
||||
#import <MobileCoreServices/MobileCoreServices.h>
|
||||
|
||||
@implementation RCTFileRequestHandler
|
||||
{
|
||||
NSOperationQueue *_fileQueue;
|
||||
}
|
||||
|
||||
@synthesize methodQueue = _methodQueue;
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
[_fileQueue cancelAllOperations];
|
||||
_fileQueue = nil;
|
||||
}
|
||||
|
||||
- (BOOL)canHandleRequest:(NSURLRequest *)request
|
||||
{
|
||||
return [request.URL.scheme caseInsensitiveCompare:@"file"] == NSOrderedSame;
|
||||
}
|
||||
|
||||
- (NSOperation *)sendRequest:(NSURLRequest *)request
|
||||
withDelegate:(id<RCTURLRequestDelegate>)delegate
|
||||
{
|
||||
// Lazy setup
|
||||
if (!_fileQueue) {
|
||||
_fileQueue = [NSOperationQueue new];
|
||||
_fileQueue.maxConcurrentOperationCount = 4;
|
||||
}
|
||||
|
||||
__block NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
|
||||
|
||||
// Get content length
|
||||
NSError *error = nil;
|
||||
NSFileManager *fileManager = [NSFileManager new];
|
||||
NSDictionary *fileAttributes = [fileManager attributesOfItemAtPath:request.URL.path error:&error];
|
||||
if (error) {
|
||||
[delegate URLRequest:op didCompleteWithError:error];
|
||||
return;
|
||||
}
|
||||
|
||||
// Get mime type
|
||||
NSString *fileExtension = [request.URL pathExtension];
|
||||
NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(
|
||||
kUTTagClassFilenameExtension, (__bridge CFStringRef)fileExtension, NULL);
|
||||
NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass(
|
||||
(__bridge CFStringRef)UTI, kUTTagClassMIMEType);
|
||||
|
||||
// Send response
|
||||
NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL
|
||||
MIMEType:contentType
|
||||
expectedContentLength:[fileAttributes[NSFileSize] ?: @-1 integerValue]
|
||||
textEncodingName:nil];
|
||||
|
||||
[delegate URLRequest:op didReceiveResponse:response];
|
||||
|
||||
// Load data
|
||||
NSData *data = [NSData dataWithContentsOfURL:request.URL
|
||||
options:NSDataReadingMappedIfSafe
|
||||
error:&error];
|
||||
if (data) {
|
||||
[delegate URLRequest:op didReceiveData:data];
|
||||
}
|
||||
[delegate URLRequest:op didCompleteWithError:error];
|
||||
}];
|
||||
|
||||
[_fileQueue addOperation:op];
|
||||
return op;
|
||||
}
|
||||
|
||||
- (void)cancelRequest:(NSOperation *)op
|
||||
{
|
||||
[op cancel];
|
||||
}
|
||||
|
||||
@end
|
@ -50,7 +50,9 @@ RCT_EXPORT_MODULE()
|
||||
static NSSet *schemes = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
schemes = [[NSSet alloc] initWithObjects:@"http", @"https", @"file", nil];
|
||||
// technically, RCTHTTPRequestHandler can handle file:// as well,
|
||||
// but it's less efficient than using RCTFileRequestHandler
|
||||
schemes = [[NSSet alloc] initWithObjects:@"http", @"https", nil];
|
||||
});
|
||||
return [schemes containsObject:request.URL.scheme.lowercaseString];
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
/* Begin PBXBuildFile section */
|
||||
1372B7371AB03E7B00659ED6 /* RCTNetInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 1372B7361AB03E7B00659ED6 /* RCTNetInfo.m */; };
|
||||
13D6D66A1B5FCF8200883BE9 /* RCTDownloadTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 13D6D6691B5FCF8200883BE9 /* RCTDownloadTask.m */; };
|
||||
13EF800E1BCBE015003F47DD /* RCTFileRequestHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 13EF800D1BCBE015003F47DD /* RCTFileRequestHandler.m */; settings = {ASSET_TAGS = (); }; };
|
||||
352DA0BA1B17855800AA15A8 /* RCTHTTPRequestHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 352DA0B81B17855800AA15A8 /* RCTHTTPRequestHandler.m */; };
|
||||
58B512081A9E6CE300147676 /* RCTNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B512071A9E6CE300147676 /* RCTNetworking.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
@ -30,6 +31,8 @@
|
||||
1372B7361AB03E7B00659ED6 /* RCTNetInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = RCTNetInfo.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
|
||||
13D6D6681B5FCF8200883BE9 /* RCTDownloadTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDownloadTask.h; sourceTree = "<group>"; };
|
||||
13D6D6691B5FCF8200883BE9 /* RCTDownloadTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDownloadTask.m; sourceTree = "<group>"; };
|
||||
13EF800C1BCBE015003F47DD /* RCTFileRequestHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTFileRequestHandler.h; sourceTree = "<group>"; };
|
||||
13EF800D1BCBE015003F47DD /* RCTFileRequestHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTFileRequestHandler.m; sourceTree = "<group>"; };
|
||||
352DA0B71B17855800AA15A8 /* RCTHTTPRequestHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTHTTPRequestHandler.h; sourceTree = "<group>"; };
|
||||
352DA0B81B17855800AA15A8 /* RCTHTTPRequestHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTHTTPRequestHandler.m; sourceTree = "<group>"; };
|
||||
58B511DB1A9E6C8500147676 /* libRCTNetwork.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTNetwork.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@ -55,6 +58,8 @@
|
||||
13D6D6691B5FCF8200883BE9 /* RCTDownloadTask.m */,
|
||||
352DA0B71B17855800AA15A8 /* RCTHTTPRequestHandler.h */,
|
||||
352DA0B81B17855800AA15A8 /* RCTHTTPRequestHandler.m */,
|
||||
13EF800C1BCBE015003F47DD /* RCTFileRequestHandler.h */,
|
||||
13EF800D1BCBE015003F47DD /* RCTFileRequestHandler.m */,
|
||||
1372B7351AB03E7B00659ED6 /* RCTNetInfo.h */,
|
||||
1372B7361AB03E7B00659ED6 /* RCTNetInfo.m */,
|
||||
58B512061A9E6CE300147676 /* RCTNetworking.h */,
|
||||
@ -130,6 +135,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
13D6D66A1B5FCF8200883BE9 /* RCTDownloadTask.m in Sources */,
|
||||
13EF800E1BCBE015003F47DD /* RCTFileRequestHandler.m in Sources */,
|
||||
1372B7371AB03E7B00659ED6 /* RCTNetInfo.m in Sources */,
|
||||
58B512081A9E6CE300147676 /* RCTNetworking.m in Sources */,
|
||||
352DA0BA1B17855800AA15A8 /* RCTHTTPRequestHandler.m in Sources */,
|
||||
|
@ -314,16 +314,17 @@ RCT_EXPORT_MODULE()
|
||||
|
||||
void (^responseBlock)(NSURLResponse *) = ^(NSURLResponse *response) {
|
||||
dispatch_async(_methodQueue, ^{
|
||||
NSHTTPURLResponse *httpResponse = nil;
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
// Might be a local file request
|
||||
httpResponse = (NSHTTPURLResponse *)response;
|
||||
NSDictionary *headers;
|
||||
NSInteger status;
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) { // Might be a local file request
|
||||
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
|
||||
headers = httpResponse.allHeaderFields ?: @{};
|
||||
status = httpResponse.statusCode;
|
||||
} else {
|
||||
headers = response.MIMEType ? @{@"Content-Type": response.MIMEType} : @{};
|
||||
status = 200;
|
||||
}
|
||||
NSArray *responseJSON = @[task.requestID,
|
||||
@(httpResponse.statusCode ?: 200),
|
||||
httpResponse.allHeaderFields ?: @{},
|
||||
];
|
||||
|
||||
NSArray *responseJSON = @[task.requestID, @(status), headers];
|
||||
[_bridge.eventDispatcher sendDeviceEventWithName:@"didReceiveNetworkResponse"
|
||||
body:responseJSON];
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user