incubator-callback-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From shaz...@apache.org
Subject [1/3] ios commit: Add support for chunked uploads to FileTransfer plugin.
Date Wed, 11 Jul 2012 21:24:31 GMT
Updated Branches:
  refs/heads/master 919d95860 -> dea6eb24d


Add support for chunked uploads to FileTransfer plugin.

Conflicts:
	CordovaLib/Classes/CDVFileTransfer.m


Project: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/commit/07f2c005
Tree: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/tree/07f2c005
Diff: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/diff/07f2c005

Branch: refs/heads/master
Commit: 07f2c0053246e73a52a4b64e710093e337d55b9f
Parents: 919d958
Author: Andrew Grieve <agrieve@chromium.org>
Authored: Mon Jul 9 22:45:50 2012 -0400
Committer: Shazron Abdullah <shazron@apache.org>
Committed: Wed Jul 11 14:23:01 2012 -0700

----------------------------------------------------------------------
 CordovaLib/Classes/CDVFileTransfer.h |    2 +
 CordovaLib/Classes/CDVFileTransfer.m |  161 ++++++++++++++++++++---------
 2 files changed, 113 insertions(+), 50 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/07f2c005/CordovaLib/Classes/CDVFileTransfer.h
----------------------------------------------------------------------
diff --git a/CordovaLib/Classes/CDVFileTransfer.h b/CordovaLib/Classes/CDVFileTransfer.h
index b9a96c9..1419434 100644
--- a/CordovaLib/Classes/CDVFileTransfer.h
+++ b/CordovaLib/Classes/CDVFileTransfer.h
@@ -42,6 +42,8 @@ typedef int CDVFileTransferDirection;
 - (void) download:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
 - (NSString*) escapePathComponentForUrlString:(NSString*)urlString;
 
+// Visible for testing.
+- (NSURLRequest*) requestForUpload:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
 -(NSMutableDictionary*) createFileTransferError:(int)code AndSource:(NSString*)source AndTarget:(NSString*)target;
 
 -(NSMutableDictionary*) createFileTransferError:(int)code 

http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/07f2c005/CordovaLib/Classes/CDVFileTransfer.m
----------------------------------------------------------------------
diff --git a/CordovaLib/Classes/CDVFileTransfer.m b/CordovaLib/Classes/CDVFileTransfer.m
index 96c6d84..4beaf42 100644
--- a/CordovaLib/Classes/CDVFileTransfer.m
+++ b/CordovaLib/Classes/CDVFileTransfer.m
@@ -19,6 +19,39 @@
 
 #import "CDV.h"
 
+#include <CFNetwork/CFNetwork.h>
+
+@interface CDVFileTransfer ()
+- (CDVFileTransferDelegate*) delegateForUpload:(NSMutableArray*)arguments;
+@end
+
+// Buffer size to use for streaming uploads.
+static const NSUInteger kStreamBufferSize = 32768;
+
+// Writes the given data to the stream in a blocking way.
+// If successful, returns bytesToWrite.
+// If the stream was closed on the other end, returns 0.
+// If there was an error, returns -1.
+static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream) {
+    UInt8* bytes = (UInt8*)[data bytes];
+    NSUInteger bytesToWrite = [data length];
+    NSUInteger totalBytesWritten = 0;
+    while (totalBytesWritten < bytesToWrite) {
+        CFIndex result = CFWriteStreamWrite(stream,
+              bytes + totalBytesWritten,
+              bytesToWrite - totalBytesWritten);
+        if (result < 0) {
+            CFStreamError error = CFWriteStreamGetError(stream);
+            NSLog(@"WriteStreamError domain: %ld error: %ld", error.domain, error.error);
+            return result;
+        } else if (result == 0) {
+            return result;
+        }
+        totalBytesWritten += result;
+    }
+    return totalBytesWritten;
+}
+
 @implementation CDVFileTransfer
 
 - (NSString*) escapePathComponentForUrlString:(NSString*)urlString
@@ -44,66 +77,54 @@
     return [urlString stringByAppendingString:pathComponent];
 }
 
-- (void) upload:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options {
+- (NSURLRequest*) requestForUpload:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
     NSString* callbackId = [arguments objectAtIndex:0];
     
     // arguments order from js: [filePath, server, fileKey, fileName, mimeType, params, debug,
chunkedMode]
     // however, params is a JavaScript object and during marshalling is put into the options
dict, 
     // thus debug and chunkedMode are the 6th and 7th arguments
     
-    NSString* filePath = (NSString*)[arguments objectAtIndex:1];
+    NSString* target = (NSString*)[arguments objectAtIndex:1];
     NSString* server = (NSString*)[arguments objectAtIndex:2];
     NSString* fileKey = (NSString*)[arguments objectAtIndex:3];
     NSString* fileName = [arguments objectAtIndex:4 withDefault:@"no-filename"];
     NSString* mimeType = [arguments objectAtIndex:5 withDefault:nil];
 //  NSString* trustAllHosts = (NSString*)[arguments objectAtIndex:6]; // allow self-signed
certs
-//  NSString* chunkedMode = (NSString*)[arguments objectAtIndex:7]; // currently unused
-    
+    BOOL chunkedMode = [[arguments objectAtIndex:7 withDefault:[NSNumber numberWithBool:YES]]
boolValue];
+
+    // CFStreamCreateBoundPair crashes on iOS < 5.
+    if (!IsAtLeastiOSVersion(@"5")) {
+        // TODO(agrieve): See if it's okay license-wise to include the work-around code from:
+        // http://developer.apple.com/library/ios/#samplecode/SimpleURLConnections/Listings/PostController_m.html
+        chunkedMode = NO;
+    }
     NSMutableDictionary* params = options;
+
+    // Extract the path part out of a file: URL.
+    NSString* filePath = [target hasPrefix:@"/"] ? [[target copy] autorelease] : [[NSURL
URLWithString:target] path];
     
     CDVPluginResult* result = nil;
     CDVFileTransferError errorCode = 0;
 
     
-    NSURL* file;
-    NSData *fileData = nil;
-    
-    if ([filePath hasPrefix:@"/"]) {
-        file = [NSURL fileURLWithPath:filePath];
-    } else {
-        file = [NSURL URLWithString:filePath];
-    }
-    
     NSURL *url = [NSURL URLWithString:[self escapePathComponentForUrlString:server]];
-    
+    NSData *fileData = nil;
     
     if (!url) {
         errorCode = INVALID_URL_ERR;
         NSLog(@"File Transfer Error: Invalid server URL %@", server);
-    } else if(![file isFileURL]) {
-        errorCode = FILE_NOT_FOUND_ERR;
-        NSLog(@"File Transfer Error: Invalid file path or URL %@", filePath);
     } else {
-        // check that file is valid
-        NSFileManager* fileMgr = [[NSFileManager alloc] init];
-        BOOL bIsDirectory = NO;
-        BOOL bExists = [fileMgr fileExistsAtPath:[file path] isDirectory:&bIsDirectory];
-        if (!bExists || bIsDirectory) {
+        NSError *err = nil;
+        // Memory map the file so that it can be read efficiently even if it is large.
+        fileData = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe
error:&err];
+        if (!fileData) {
             errorCode = FILE_NOT_FOUND_ERR;
-        } else {
-            // file exists, make sure we can get the data
-            fileData = [NSData dataWithContentsOfURL:file];
-            
-            if(!fileData) {
-                errorCode =  FILE_NOT_FOUND_ERR;
-                NSLog(@"File Transfer Error: Could not read file data %@", filePath);
-            }
+            NSLog(@"File Transfer Error: Could not read file data %@", filePath);
         }
-        [fileMgr release];
     }
     
     if(errorCode > 0) {
-        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:
[self createFileTransferError:errorCode AndSource:filePath AndTarget:server]];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:
[self createFileTransferError:errorCode AndSource:target AndTarget:server]];
         
         [self writeJavascript:[result toErrorCallbackString:callbackId]];
         return;
@@ -153,10 +174,10 @@
         [req setValue:val forHTTPHeaderField:nkey];	
     }
     
-	NSMutableData *postBody = [NSMutableData data];
+	NSMutableData *postBodyBeforeFile = [NSMutableData data];
 	enumerator = [params keyEnumerator];
-	id key;
 	
+	id key;
 	while ((key = [enumerator nextObject])) {
 		val = [params objectForKey:key];
 		if(!val || val == [NSNull null]) {
@@ -171,36 +192,76 @@
 			continue;
 		}
 		
-		[postBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
-		[postBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n",
key] dataUsingEncoding:NSUTF8StringEncoding]];
-		[postBody appendData:[val dataUsingEncoding:NSUTF8StringEncoding]];
-		[postBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
+		[postBodyBeforeFile appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
+		[postBodyBeforeFile appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data;
name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
+		[postBodyBeforeFile appendData:[val dataUsingEncoding:NSUTF8StringEncoding]];
+		[postBodyBeforeFile appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
 	}
     
-	[postBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
-	[postBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\";
filename=\"%@\"\r\n", fileKey, fileName] dataUsingEncoding:NSUTF8StringEncoding]];
+	[postBodyBeforeFile appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
+	[postBodyBeforeFile appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data;
name=\"%@\"; filename=\"%@\"\r\n", fileKey, fileName] dataUsingEncoding:NSUTF8StringEncoding]];
     if (mimeType != nil) {
-        [postBody appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n", mimeType]
dataUsingEncoding:NSUTF8StringEncoding]];
+        [postBodyBeforeFile appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n",
mimeType] dataUsingEncoding:NSUTF8StringEncoding]];
     }
-    [postBody appendData:[[NSString stringWithFormat:@"Content-Length: %d\r\n\r\n", [fileData
length]] dataUsingEncoding:NSUTF8StringEncoding]];
+    [postBodyBeforeFile appendData:[[NSString stringWithFormat:@"Content-Length: %d\r\n\r\n",
[fileData length]] dataUsingEncoding:NSUTF8StringEncoding]];
 
     DLog(@"fileData length: %d", [fileData length]);
-	[postBody appendData:fileData];
-	[postBody appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
-    
-    //[req setValue:[[NSNumber numberWithInteger:[postBody length]] stringValue] forHTTPHeaderField:@"Content-Length"];
-	[req setHTTPBody:postBody];
+ 	NSData *postBodyAfterFile = [[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding];
+
     
+    NSUInteger totalPayloadLength = [postBodyBeforeFile length] + [fileData length] + [postBodyAfterFile
length];
+    [req setValue:[[NSNumber numberWithInteger:totalPayloadLength] stringValue] forHTTPHeaderField:@"Content-Length"];
+
 	
+    
+    if (chunkedMode) {
+        CFReadStreamRef readStream = NULL;
+        CFWriteStreamRef writeStream = NULL;
+        CFStreamCreateBoundPair(NULL, &readStream, &writeStream, kStreamBufferSize);
+        [req setHTTPBodyStream:CFBridgingRelease(readStream)];
+
+        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            if (CFWriteStreamOpen(writeStream)) {
+                NSData* chunks[] = { postBodyBeforeFile, fileData, postBodyAfterFile };
+                int numChunks = sizeof(chunks) / sizeof(chunks[0]);
+                for (int i = 0; i < numChunks; ++i) {
+                    CFIndex result = WriteDataToStream(chunks[i], writeStream);
+                    if (result <= 0) {
+                        break;
+                    }
+                }
+            } else {
+                NSLog(@"FileTransfer: Failed to open writeStream");
+            }
+            CFWriteStreamClose(writeStream);
+            CFRelease(writeStream);
+        });
+    } else {
+        [postBodyBeforeFile appendData:fileData];
+        [postBodyBeforeFile appendData:postBodyAfterFile];
+    	[req setHTTPBody:postBodyBeforeFile];
+    }    
+	return req;
+}
+
+- (CDVFileTransferDelegate*) delegateForUpload:(NSMutableArray*)arguments {
+    NSString* callbackId = [arguments objectAtIndex:0];
+    NSString* target = (NSString*)[arguments objectAtIndex:1];
+    NSString* server = (NSString*)[arguments objectAtIndex:2];
+
 	CDVFileTransferDelegate* delegate = [[[CDVFileTransferDelegate alloc] init] autorelease];
 	delegate.command = self;
     delegate.direction = CDV_TRANSFER_UPLOAD;
     delegate.callbackId = callbackId;
     delegate.source = server;
-    delegate.target = filePath;
-	
+    delegate.target = target;
+    return delegate;
+}
+
+- (void) upload:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options {
+    NSURLRequest* req = [self requestForUpload:arguments withDict:options];
+    CDVFileTransferDelegate* delegate = [self delegateForUpload:arguments];
 	[NSURLConnection connectionWithRequest:req delegate:delegate];
-    
 }
 
 - (void) download:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options {


Mime
View raw message