mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +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:
committed by
facebook-github-bot-4
parent
28f6eba22d
commit
a92f107712
@@ -86,7 +86,7 @@ RCT_EXPORT_MODULE()
|
|||||||
}
|
}
|
||||||
|
|
||||||
RCTDownloadTask *task = [_bridge.networking downloadTaskWithRequest:request completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) {
|
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;
|
RCTImageDownloader *strongSelf = weakSelf;
|
||||||
NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data userInfo:nil storagePolicy:NSURLCacheStorageAllowed];
|
NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data userInfo:nil storagePolicy:NSURLCacheStorageAllowed];
|
||||||
[strongSelf->_cache storeCachedResponse:cachedResponse forRequest:request];
|
[strongSelf->_cache storeCachedResponse:cachedResponse forRequest:request];
|
||||||
@@ -106,9 +106,15 @@ RCT_EXPORT_MODULE()
|
|||||||
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
|
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
|
||||||
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
|
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
|
||||||
{
|
{
|
||||||
if ([imageURL.scheme.lowercaseString hasPrefix:@"http"]) {
|
if ([imageURL.scheme.lowercaseString hasPrefix:@"http"] ||
|
||||||
|
[imageURL.scheme caseInsensitiveCompare:@"file"] == NSOrderedSame) {
|
||||||
__block RCTImageLoaderCancellationBlock decodeCancel = nil;
|
__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;
|
__weak RCTImageDownloader *weakSelf = self;
|
||||||
RCTImageLoaderCancellationBlock downloadCancel = [self downloadDataForURL:imageURL progressHandler:progressHandler completionHandler:^(NSError *error, NSData *imageData) {
|
RCTImageLoaderCancellationBlock downloadCancel = [self downloadDataForURL:imageURL progressHandler:progressHandler completionHandler:^(NSError *error, NSData *imageData) {
|
||||||
if (error) {
|
if (error) {
|
||||||
@@ -153,31 +159,6 @@ RCT_EXPORT_MODULE()
|
|||||||
return ^{
|
return ^{
|
||||||
cancelled = YES;
|
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 {
|
} else {
|
||||||
RCTLogError(@"Unexpected image schema %@", imageURL.scheme);
|
RCTLogError(@"Unexpected image schema %@", imageURL.scheme);
|
||||||
return ^{};
|
return ^{};
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
#import "RCTDownloadTask.h"
|
#import "RCTDownloadTask.h"
|
||||||
|
|
||||||
#import "RCTAssert.h"
|
#import "RCTLog.h"
|
||||||
|
|
||||||
@implementation RCTDownloadTask
|
@implementation RCTDownloadTask
|
||||||
{
|
{
|
||||||
@@ -65,8 +65,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||||||
{
|
{
|
||||||
if (![requestToken isEqual:_requestToken]) {
|
if (![requestToken isEqual:_requestToken]) {
|
||||||
if (RCT_DEBUG) {
|
if (RCT_DEBUG) {
|
||||||
RCTAssert([requestToken isEqual:_requestToken],
|
RCTLogError(@"Unrecognized request token: %@ expected: %@", requestToken, _requestToken);
|
||||||
@"Unrecognized request token: %@", requestToken);
|
|
||||||
}
|
}
|
||||||
if (_completionBlock) {
|
if (_completionBlock) {
|
||||||
_completionBlock(_response, _data, [NSError errorWithDomain:RCTErrorDomain code:0
|
_completionBlock(_response, _data, [NSError errorWithDomain:RCTErrorDomain code:0
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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 NSSet *schemes = nil;
|
||||||
static dispatch_once_t onceToken;
|
static dispatch_once_t onceToken;
|
||||||
dispatch_once(&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];
|
return [schemes containsObject:request.URL.scheme.lowercaseString];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
1372B7371AB03E7B00659ED6 /* RCTNetInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 1372B7361AB03E7B00659ED6 /* RCTNetInfo.m */; };
|
1372B7371AB03E7B00659ED6 /* RCTNetInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 1372B7361AB03E7B00659ED6 /* RCTNetInfo.m */; };
|
||||||
13D6D66A1B5FCF8200883BE9 /* RCTDownloadTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 13D6D6691B5FCF8200883BE9 /* RCTDownloadTask.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 */; };
|
352DA0BA1B17855800AA15A8 /* RCTHTTPRequestHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 352DA0B81B17855800AA15A8 /* RCTHTTPRequestHandler.m */; };
|
||||||
58B512081A9E6CE300147676 /* RCTNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B512071A9E6CE300147676 /* RCTNetworking.m */; };
|
58B512081A9E6CE300147676 /* RCTNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B512071A9E6CE300147676 /* RCTNetworking.m */; };
|
||||||
/* End PBXBuildFile section */
|
/* 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; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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; };
|
58B511DB1A9E6C8500147676 /* libRCTNetwork.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTNetwork.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
@@ -55,6 +58,8 @@
|
|||||||
13D6D6691B5FCF8200883BE9 /* RCTDownloadTask.m */,
|
13D6D6691B5FCF8200883BE9 /* RCTDownloadTask.m */,
|
||||||
352DA0B71B17855800AA15A8 /* RCTHTTPRequestHandler.h */,
|
352DA0B71B17855800AA15A8 /* RCTHTTPRequestHandler.h */,
|
||||||
352DA0B81B17855800AA15A8 /* RCTHTTPRequestHandler.m */,
|
352DA0B81B17855800AA15A8 /* RCTHTTPRequestHandler.m */,
|
||||||
|
13EF800C1BCBE015003F47DD /* RCTFileRequestHandler.h */,
|
||||||
|
13EF800D1BCBE015003F47DD /* RCTFileRequestHandler.m */,
|
||||||
1372B7351AB03E7B00659ED6 /* RCTNetInfo.h */,
|
1372B7351AB03E7B00659ED6 /* RCTNetInfo.h */,
|
||||||
1372B7361AB03E7B00659ED6 /* RCTNetInfo.m */,
|
1372B7361AB03E7B00659ED6 /* RCTNetInfo.m */,
|
||||||
58B512061A9E6CE300147676 /* RCTNetworking.h */,
|
58B512061A9E6CE300147676 /* RCTNetworking.h */,
|
||||||
@@ -130,6 +135,7 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
13D6D66A1B5FCF8200883BE9 /* RCTDownloadTask.m in Sources */,
|
13D6D66A1B5FCF8200883BE9 /* RCTDownloadTask.m in Sources */,
|
||||||
|
13EF800E1BCBE015003F47DD /* RCTFileRequestHandler.m in Sources */,
|
||||||
1372B7371AB03E7B00659ED6 /* RCTNetInfo.m in Sources */,
|
1372B7371AB03E7B00659ED6 /* RCTNetInfo.m in Sources */,
|
||||||
58B512081A9E6CE300147676 /* RCTNetworking.m in Sources */,
|
58B512081A9E6CE300147676 /* RCTNetworking.m in Sources */,
|
||||||
352DA0BA1B17855800AA15A8 /* RCTHTTPRequestHandler.m in Sources */,
|
352DA0BA1B17855800AA15A8 /* RCTHTTPRequestHandler.m in Sources */,
|
||||||
|
|||||||
@@ -314,16 +314,17 @@ RCT_EXPORT_MODULE()
|
|||||||
|
|
||||||
void (^responseBlock)(NSURLResponse *) = ^(NSURLResponse *response) {
|
void (^responseBlock)(NSURLResponse *) = ^(NSURLResponse *response) {
|
||||||
dispatch_async(_methodQueue, ^{
|
dispatch_async(_methodQueue, ^{
|
||||||
NSHTTPURLResponse *httpResponse = nil;
|
NSDictionary *headers;
|
||||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
NSInteger status;
|
||||||
// Might be a local file request
|
if ([response isKindOfClass:[NSHTTPURLResponse class]]) { // Might be a local file request
|
||||||
httpResponse = (NSHTTPURLResponse *)response;
|
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
|
||||||
|
headers = httpResponse.allHeaderFields ?: @{};
|
||||||
|
status = httpResponse.statusCode;
|
||||||
|
} else {
|
||||||
|
headers = response.MIMEType ? @{@"Content-Type": response.MIMEType} : @{};
|
||||||
|
status = 200;
|
||||||
}
|
}
|
||||||
NSArray *responseJSON = @[task.requestID,
|
NSArray *responseJSON = @[task.requestID, @(status), headers];
|
||||||
@(httpResponse.statusCode ?: 200),
|
|
||||||
httpResponse.allHeaderFields ?: @{},
|
|
||||||
];
|
|
||||||
|
|
||||||
[_bridge.eventDispatcher sendDeviceEventWithName:@"didReceiveNetworkResponse"
|
[_bridge.eventDispatcher sendDeviceEventWithName:@"didReceiveNetworkResponse"
|
||||||
body:responseJSON];
|
body:responseJSON];
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user