mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-05-25 14:47:21 +08:00
Add multipart response download task (2nd edition)
Reviewed By: mmmulani Differential Revision: D3976605 fbshipit-source-id: c15cc859aa1288e831f70256566f743f4a8d9cd2
This commit is contained in:
committed by
Facebook Github Bot
parent
584bd0da24
commit
84eaeb0adf
119
React/Base/RCTMultipartDataTask.m
Normal file
119
React/Base/RCTMultipartDataTask.m
Normal file
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* 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 "RCTMultipartDataTask.h"
|
||||
|
||||
@interface RCTMultipartDataTask () <NSURLSessionDataDelegate, NSURLSessionDataDelegate>
|
||||
|
||||
@end
|
||||
|
||||
// We need this ugly runtime check because [streamTask captureStreams] below fails on iOS version
|
||||
// earlier than 9.0. Unfortunately none of the proper ways of checking worked:
|
||||
//
|
||||
// - NSURLSessionStreamTask class is available and is not Null on iOS 8
|
||||
// - [[NSURLSessionStreamTask new] respondsToSelector:@selector(captureStreams)] is always NO
|
||||
// - The instance we get in URLSession:dataTask:didBecomeStreamTask: is of __NSCFURLLocalStreamTaskFromDataTask
|
||||
// and it responds to captureStreams on iOS 9+ but doesn't on iOS 8. Which means we can't get direct access
|
||||
// to the streams on iOS 8 and at that point it's too late to change the behavior back to dataTask
|
||||
// - The compile-time #ifdef's can't be used because an app compiled for iOS8 can still run on iOS9
|
||||
|
||||
static BOOL isStreamTaskSupported() {
|
||||
return [[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){9,0,0}];
|
||||
}
|
||||
|
||||
@implementation RCTMultipartDataTask {
|
||||
NSURL *_url;
|
||||
RCTMultipartDataTaskCallback _partHandler;
|
||||
NSInteger _statusCode;
|
||||
NSDictionary *_headers;
|
||||
NSString *_boundary;
|
||||
NSMutableData *_data;
|
||||
}
|
||||
|
||||
- (instancetype)initWithURL:(NSURL *)url partHandler:(RCTMultipartDataTaskCallback)partHandler
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_url = url;
|
||||
_partHandler = [partHandler copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)startTask
|
||||
{
|
||||
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
|
||||
delegate:self delegateQueue:nil];
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:_url];
|
||||
if (isStreamTaskSupported()) {
|
||||
[request addValue:@"multipart/mixed" forHTTPHeaderField:@"Accept"];
|
||||
}
|
||||
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
|
||||
[dataTask resume];
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
|
||||
{
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
|
||||
_headers = [httpResponse allHeaderFields];
|
||||
_statusCode = [httpResponse statusCode];
|
||||
|
||||
NSString *contentType = @"";
|
||||
for (NSString *key in [_headers keyEnumerator]) {
|
||||
if ([[key lowercaseString] isEqualToString:@"content-type"]) {
|
||||
contentType = [_headers valueForKey:key];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"multipart/mixed;.*boundary=\"([^\"]+)\"" options:0 error:nil];
|
||||
NSTextCheckingResult *match = [regex firstMatchInString:contentType options:0 range:NSMakeRange(0, contentType.length)];
|
||||
if (match) {
|
||||
_boundary = [contentType substringWithRange:[match rangeAtIndex:1]];
|
||||
completionHandler(NSURLSessionResponseBecomeStream);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// In case the server doesn't support multipart/mixed responses, fallback to normal download
|
||||
_data = [[NSMutableData alloc] initWithCapacity:1024 * 1024];
|
||||
completionHandler(NSURLSessionResponseAllow);
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
|
||||
{
|
||||
_partHandler(_statusCode, _headers, _data, error, YES);
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
|
||||
{
|
||||
[_data appendData:data];
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeStreamTask:(NSURLSessionStreamTask *)streamTask
|
||||
{
|
||||
[streamTask captureStreams];
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session streamTask:(NSURLSessionStreamTask *)streamTask didBecomeInputStream:(NSInputStream *)inputStream outputStream:(NSOutputStream *)outputStream
|
||||
{
|
||||
RCTMultipartStreamReader *reader = [[RCTMultipartStreamReader alloc] initWithInputStream:inputStream boundary:_boundary];
|
||||
RCTMultipartDataTaskCallback partHandler = _partHandler;
|
||||
NSInteger statusCode = _statusCode;
|
||||
|
||||
BOOL completed = [reader readAllParts:^(NSDictionary *headers, NSData *content, BOOL done) {
|
||||
partHandler(statusCode, headers, content, nil, done);
|
||||
}];
|
||||
if (!completed) {
|
||||
partHandler(statusCode, nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:nil], YES);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user