大家都知道在 App 上傳檔案時顯示進度條是讓使用者知道上傳進度是友善的設計,某些情況下當你開發 iOS App 需要上傳檔案到 Azure Blob Storage 卻可能不知道該怎麼下手,本篇文章能讓你和你的團隊看到一絲曙光。
不熟 Objective-C 和 Cocoa Framework 的你 (好啦就是我本人怎樣) 可能會嘗試著以物件導向的方式繼承 NSInputStream 以責任鍊的方式攔截讀取方法,取得目前位置和檔案長度,但多次嘗試後你會很挫折。想要以 Observer 或 NotificationCenter 的方式解決也無從下手。
試了好久我都差不多快要放棄了哈哈。
回到正題,原不具上傳進度資訊的原程式碼如下:
#import <AZSClient/AZSClient.h>
NSString *const AZURE_STORAGE_CONNECTION_STRING = @"DefaultEndpointsProtocol=https;AccountName=(your account name);AccountKey=(your account key);EndpointSuffix=core.windows.net";
NSString *const AZURE_STORAGE_CONTAINER = @"files";
NSString *const LOCAL_FILE_FILEPATH = @"/Users/dino/A.mp4";
NSString *const LOCAL_FILE_FILENAME = @"A.mp4";
NSError *error;
AZSCloudStorageAccount *account = [AZSCloudStorageAccount accountFromConnectionString:AZURE_STORAGE_CONNECTION_STRING error:&error];
AZSCloudBlobClient *client = [account getBlobClient];
AZSCloudBlobContainer *container = [client containerReferenceFromName:AZURE_STORAGE_CONTAINER];
[container createContainerIfNotExistsWithCompletionHandler:^(NSError *error, BOOL exists) {
@autoreleasepool {
AZSCloudBlockBlob *blob = [container blockBlobReferenceFromName:LOCAL_FILE_FILENAME];
NSInputStream *stream = [NSInputStream inputStreamWithFileAtPath:LOCAL_FILE_FILEPATH];
NSLog(@"upload started.");
[blob uploadFromStream:stream completionHandler:^(NSError *error) {
NSLog(@"upload done.");
exit(0);
}];
}
}];
解法在原始碼中,讀完程式碼後我們發現 AZSOperationContext 類別提供我們解套的方式。AZSOperationContext 提供第三方介入程式庫,提供事件、預設屬性、日誌等讓開發人員得以自訂的機會。
我們可以運用 sendRequest 事件,透過 NSStreamFileCurrentOffsetKey 讀取的 NSInputStream 的指標位置,搭配已知的檔案長度,即可計算得出完成百分比。
#import <AZSClient/AZSClient.h>
NSString *const AZURE_STORAGE_CONNECTION_STRING = @"DefaultEndpointsProtocol=https;AccountName=(your account name);AccountKey=(your account key);EndpointSuffix=core.windows.net";
NSString *const AZURE_STORAGE_CONTAINER = @"files";
NSString *const LOCAL_FILE_FILEPATH = @"/Users/dino/A.mp4";
NSString *const LOCAL_FILE_FILENAME = @"A.mp4";
NSError *error;
AZSCloudStorageAccount *account = [AZSCloudStorageAccount accountFromConnectionString:AZURE_STORAGE_CONNECTION_STRING error:&error];
AZSCloudBlobClient *client = [account getBlobClient];
AZSCloudBlobContainer *container = [client containerReferenceFromName:AZURE_STORAGE_CONTAINER];
[container createContainerIfNotExistsWithCompletionHandler:^(NSError *error, BOOL exists) {
@autoreleasepool {
AZSCloudBlockBlob *blob = [container blockBlobReferenceFromName:LOCAL_FILE_FILENAME];
NSUInteger size = [[[NSFileManager defaultManager] attributesOfItemAtPath:LOCAL_FILE_FILEPATH error:&error] fileSize];
NSInputStream *stream = [NSInputStream inputStreamWithFileAtPath:LOCAL_FILE_FILEPATH];
AZSOperationContext *operationContext = [[AZSOperationContext alloc] init];
operationContext.sendingRequest = ^(NSMutableURLRequest *request, AZSOperationContext *context) {
NSNumber *number = [stream propertyForKey:NSStreamFileCurrentOffsetKey];
if (number != nil) {
NSUInteger pos = [number longValue];
float percentage = ((float)pos / size) * 100;
NSLog(@"current %ld of %ld, %f%% completed", pos, size, percentage);
}
};
NSLog(@"upload started.");
[blob uploadFromStream:stream accessCondition:nil requestOptions:nil operationContext:operationContext completionHandler:^(NSError *error) {
NSLog(@"upload done.");
exit(0);
}];
}
}];