diff --git a/plugin.xml b/plugin.xml index 8d6bafe..7513bb0 100644 --- a/plugin.xml +++ b/plugin.xml @@ -2,7 +2,7 @@ + version="0.4.0-dev"> File Transfer Cordova File Transfer Plugin Apache 2.0 diff --git a/src/ios/CDVFileTransfer.h b/src/ios/CDVFileTransfer.h index 8e8211d..aa1cf9e 100644 --- a/src/ios/CDVFileTransfer.h +++ b/src/ios/CDVFileTransfer.h @@ -71,6 +71,7 @@ extern NSString* const kOptionsKeyCookie; @property (nonatomic, copy) NSString* objectId; @property (nonatomic, copy) NSString* source; @property (nonatomic, copy) NSString* target; +@property (nonatomic, copy) NSURL* targetURL; @property (nonatomic, copy) NSString* mimeType; @property (assign) int responseCode; // atomic @property (nonatomic, assign) long long bytesTransfered; diff --git a/src/ios/CDVFileTransfer.m b/src/ios/CDVFileTransfer.m index f1fd4d3..2eec342 100644 --- a/src/ios/CDVFileTransfer.m +++ b/src/ios/CDVFileTransfer.m @@ -20,12 +20,15 @@ #import #import "CDVFileTransfer.h" #import "CDVFile.h" +#import "CDVLocalFilesystem.h" #import #import #import #import +extern CDVFile *filePlugin; + @interface CDVFileTransfer () // Sets the requests headers for the request. - (void)applyRequestHeaders:(NSDictionary*)headers toRequest:(NSMutableURLRequest*)req; @@ -270,52 +273,48 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream) - (void)fileDataForUploadCommand:(CDVInvokedUrlCommand*)command { - NSString* target = (NSString*)[command.arguments objectAtIndex:0]; + NSString* source = (NSString*)[command.arguments objectAtIndex:0]; + NSString* server = [command.arguments objectAtIndex:1]; NSError* __autoreleasing err = nil; - // return unsupported result for assets-library URLs - if ([target hasPrefix:kCDVAssetsLibraryPrefix]) { - // Instead, we return after calling the asynchronous method and send `result` in each of the blocks. - ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset* asset) { - if (asset) { - // We have the asset! Get the data and send it off. - ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation]; - Byte* buffer = (Byte*)malloc([assetRepresentation size]); - NSUInteger bufferSize = [assetRepresentation getBytes:buffer fromOffset:0.0 length:[assetRepresentation size] error:nil]; - NSData* fileData = [NSData dataWithBytesNoCopy:buffer length:bufferSize freeWhenDone:YES]; - [self uploadData:fileData command:command]; - } else { + CDVFilesystemURL *sourceURL = [CDVFilesystemURL fileSystemURLWithString:source]; + NSObject *fs; + if (sourceURL) { + // Try to get a CDVFileSystem which will handle this file. + // This requires talking to the current CDVFile plugin. + fs = [filePlugin filesystemForURL:sourceURL]; + } + if (fs) { + [fs readFileAtURL:sourceURL start:0 end:-1 callback:^(NSData *fileData, NSString *mimeType, CDVFileError err) { + if (err) { // We couldn't find the asset. Send the appropriate error. - CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR]; + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[self createFileTransferError:NOT_FOUND_ERR AndSource:source AndTarget:server]]; [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + } else { + [self uploadData:fileData command:command]; } - }; - ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError* error) { - // Retrieving the asset failed for some reason. Send the appropriate error. - CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]]; - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; - }; - - ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init]; - [assetsLibrary assetForURL:[NSURL URLWithString:target] resultBlock:resultBlock failureBlock:failureBlock]; + }]; return; } else { // Extract the path part out of a file: URL. - NSString* filePath = [target hasPrefix:@"/"] ? [target copy] : [[NSURL URLWithString:target] path]; + NSString* filePath = [source hasPrefix:@"/"] ? [source copy] : [[NSURL URLWithString:source] path]; if (filePath == nil) { // We couldn't find the asset. Send the appropriate error. - CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR]; + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[self createFileTransferError:NOT_FOUND_ERR AndSource:source AndTarget:server]]; [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; return; } - + // Memory map the file so that it can be read efficiently even if it is large. NSData* fileData = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:&err]; - + if (err != nil) { - NSLog(@"Error opening file %@: %@", target, err); + NSLog(@"Error opening file %@: %@", source, err); + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[self createFileTransferError:NOT_FOUND_ERR AndSource:source AndTarget:server]]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + } else { + [self uploadData:fileData command:command]; } - [self uploadData:fileData command:command]; } } @@ -359,47 +358,43 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream) - (void)download:(CDVInvokedUrlCommand*)command { DLog(@"File Transfer downloading file..."); - NSString* sourceUrl = [command.arguments objectAtIndex:0]; - NSString* filePath = [command.arguments objectAtIndex:1]; + NSString* source = [command.arguments objectAtIndex:0]; + NSString* target = [command.arguments objectAtIndex:1]; BOOL trustAllHosts = [[command.arguments objectAtIndex:2 withDefault:[NSNumber numberWithBool:YES]] boolValue]; // allow self-signed certs NSString* objectId = [command.arguments objectAtIndex:3]; NSDictionary* headers = [command.arguments objectAtIndex:4 withDefault:nil]; - // return unsupported result for assets-library URLs - if ([filePath hasPrefix:kCDVAssetsLibraryPrefix]) { - CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"download not supported for assets-library URLs."]; - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; - return; - } - CDVPluginResult* result = nil; CDVFileTransferError errorCode = 0; - NSURL* file; + NSURL* targetURL; - if ([filePath hasPrefix:@"/"]) { - file = [NSURL fileURLWithPath:filePath]; + if ([target hasPrefix:@"/"]) { + targetURL = [NSURL fileURLWithPath:target]; } else { - file = [NSURL URLWithString:filePath]; + targetURL = [NSURL URLWithString:target]; } - NSURL* url = [NSURL URLWithString:sourceUrl]; + NSURL* sourceURL = [NSURL URLWithString:source]; - if (!url) { + if (!sourceURL) { errorCode = INVALID_URL_ERR; - NSLog(@"File Transfer Error: Invalid server URL %@", sourceUrl); - } else if (![file isFileURL]) { - errorCode = FILE_NOT_FOUND_ERR; - NSLog(@"File Transfer Error: Invalid file path or URL %@", filePath); + NSLog(@"File Transfer Error: Invalid server URL %@", source); + } else if (![targetURL isFileURL]) { + CDVFilesystemURL *fsURL = [CDVFilesystemURL fileSystemURLWithString:target]; + if (!fsURL) { + errorCode = FILE_NOT_FOUND_ERR; + NSLog(@"File Transfer Error: Invalid file path or URL %@", target); + } } if (errorCode > 0) { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[self createFileTransferError:errorCode AndSource:sourceUrl AndTarget:filePath]]; + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[self createFileTransferError:errorCode AndSource:source AndTarget:target]]; [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; return; } - NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL:url]; + NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL:sourceURL]; [self applyRequestHeaders:headers toRequest:req]; CDVFileTransferDelegate* delegate = [[CDVFileTransferDelegate alloc] init]; @@ -407,8 +402,9 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream) delegate.direction = CDV_TRANSFER_DOWNLOAD; delegate.callbackId = command.callbackId; delegate.objectId = objectId; - delegate.source = sourceUrl; - delegate.target = filePath; + delegate.source = source; + delegate.target = target; + delegate.targetURL = targetURL; delegate.trustAllHosts = trustAllHosts; delegate.connection = [NSURLConnection connectionWithRequest:req delegate:delegate]; @@ -521,8 +517,6 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream) NSString* downloadResponse = nil; NSMutableDictionary* uploadResult; CDVPluginResult* result = nil; - BOOL bDirRequest = NO; - CDVFile* file; NSLog(@"File Transfer Finished with response code %d", self.responseCode); @@ -548,8 +542,7 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream) self.targetFileHandle = nil; DLog(@"File Transfer Download success"); - file = [[CDVFile alloc] init]; - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[file getDirectoryEntry:target isDirectory:bDirRequest]]; + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[filePlugin makeEntryForURL:self.targetURL]]; } else { downloadResponse = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding]; result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:CONNECTION_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode AndBody:downloadResponse]]; @@ -618,7 +611,23 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream) } if ((self.direction == CDV_TRANSFER_DOWNLOAD) && (self.responseCode >= 200) && (self.responseCode < 300)) { // Download response is okay; begin streaming output to file - NSString* parentPath = [self.target stringByDeletingLastPathComponent]; + NSString *filePath = nil; + CDVFilesystemURL *sourceURL = [CDVFilesystemURL fileSystemURLWithString:self.target]; + if (sourceURL && sourceURL.fileSystemType != -1) { + // This requires talking to the current CDVFile plugin + NSObject *fs = [filePlugin.fileSystems objectAtIndex:sourceURL.fileSystemType]; + filePath = [fs filesystemPathForURL:sourceURL]; + } else { + // Extract the path part out of a file: URL. + NSString* filePath = [self.target hasPrefix:@"/"] ? [self.target copy] : [[NSURL URLWithString:self.target] path]; + if (filePath == nil) { + // We couldn't find the asset. Send the appropriate error. + [self cancelTransferWithError:connection errorMessage:[NSString stringWithFormat:@"Could not create target file"]]; + return; + } + } + + NSString* parentPath = [filePath stringByDeletingLastPathComponent]; // create parent directories if needed if ([[NSFileManager defaultManager] createDirectoryAtPath:parentPath withIntermediateDirectories:YES attributes:nil error:&error] == NO) { @@ -630,16 +639,16 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream) return; } // create target file - if ([[NSFileManager defaultManager] createFileAtPath:self.target contents:nil attributes:nil] == NO) { + if ([[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil] == NO) { [self cancelTransferWithError:connection errorMessage:@"Could not create target file"]; return; } // open target file for writing - self.targetFileHandle = [NSFileHandle fileHandleForWritingAtPath:self.target]; + self.targetFileHandle = [NSFileHandle fileHandleForWritingAtPath:filePath]; if (self.targetFileHandle == nil) { [self cancelTransferWithError:connection errorMessage:@"Could not open target file for writing"]; } - DLog(@"Streaming to file %@", target); + DLog(@"Streaming to file %@", filePath); } } diff --git a/www/FileTransfer.js b/www/FileTransfer.js index 66a1c4b..d6690fe 100644 --- a/www/FileTransfer.js +++ b/www/FileTransfer.js @@ -184,6 +184,7 @@ FileTransfer.prototype.download = function(source, target, successCallback, erro entry.isFile = result.isFile; entry.name = result.name; entry.fullPath = result.fullPath; + entry.filesystem = new FileSystem(result.filesystem == window.PERSISTENT ? 'persistent' : 'temporary'); successCallback(entry); } };