爲七牛iOS SDK添加ALAsset上傳支持

本blog除部分譯文外,全部內容均爲原創,若有雷同,算我抄你:-)html

Update:七牛官方SDK已經加入了對ALAsset的支持。

問題描述

七牛iOS SDK的上傳API只有兩個ios

objc@interface QNUploadManager : NSObject

- (void)putData:(NSData *)data
            key:(NSString *)key
          token:(NSString *)token
       complete:(QNUpCompletionHandler)completionHandler
         option:(QNUploadOption *)option;

- (void)putFile:(NSString *)filePath
            key:(NSString *)key
          token:(NSString *)token
       complete:(QNUpCompletionHandler)completionHandler
         option:(QNUploadOption *)option;

@end

其中putFileXXX是針對文件上傳的,這個方法內部是依賴NSFileManager來獲取文件信息的git

objcNSError *error = nil;
NSDictionary *fileAttr = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:&error];

NSNumber *fileSizeNumber = fileAttr[NSFileSize];
UInt32 fileSize = [fileSizeNumber intValue];
NSData *data = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:&error];

那麼問題來了,對於ALAsset,即系統相冊中的圖片或視頻,獲取到的assetURL是相似於以下形式的:github

shassets-library://asset/asset.MOV?id=A16D4A3B-664E-4A75-90E8-37EA3F04FF2E&ext=MOV

NSFileManager沒法處理,於是沒法正確獲取文件大小等信息,更不用說上傳了。app

解決方案

爲便於說明,假定有ALAsset實例asset。
首先,經過asset.defaultRepresentation.size可以獲取到對應文件的大小。爲QNUploadManager建立一個category,以下ide

objc@interface QNUploadManager (ALAssetSupport)

- (void)putALasset:(ALAsset *)asset
               key:(NSString *)key
             token:(NSString *)token
          complete:(QNUpCompletionHandler)completionHandler
            option:(QNUploadOption *)option;


@end

具體實現以下post

objc- (void)putALasset:(ALAsset *)asset
               key:(NSString *)key
             token:(NSString *)token
          complete:(QNUpCompletionHandler)completionHandler
            option:(QNUploadOption *)option
{
    //other code...


        QNResumeUpload *up = [[QNResumeUpload alloc]
                              initWithData:nil
                              withSize:(UInt32)asset.defaultRepresentation.size
                              withKey:key
                              withToken:token
                              withCompletionHandler:complete
                              withOption:option
                              withModifyTime:modifyTime
                              withRecorder:recorder
                              withRecorderKey:recorderKey
                              withHttpManager:[self getHttpManagerProperty]];
        up.asset = asset;
    }
}

從上面的代碼能夠看到,QNUploadManager實際上只是獲取文件信息,作一些預處理,而真正的上傳過程是由QNResumeUpload完成的。QNResumeUpload的初始化入參不少,須要注意的是data和size,一個簡化版的initXXX以下atom

objc- (instancetype)initWithData:(NSData *)data
                    withSize:(UInt32)size
                     withOtherParameters:(XXX *)XXX;

其中data是文件句柄打開後的二進制數據,size是數據長度。
你必定已經發現我在putALassetXXX裏很弱智地在data這個入參上傳入了nil,這是有緣由的。
打開QNResumeUpload.m,搜索data後發現,data自己只在2個獲取分塊數據的方法裏涉及到url

objc- (void)makeBlock:(NSString *)uphost
           offset:(UInt32)offset
        blockSize:(UInt32)blockSize
        chunkSize:(UInt32)chunkSize
         progress:(QNInternalProgressBlock)progressBlock
         complete:(QNCompleteBlock)complete;

- (void)putChunk:(NSString *)uphost
          offset:(UInt32)offset
            size:(UInt32)size
         context:(NSString *)context
        progress:(QNInternalProgressBlock)progressBlock
        complete:(QNCompleteBlock)complete; 
}

只要override它們,讓它們支持ALAsset便可。
首先爲QNResumeUpload添加propertycode

objc@class ALAsset;

@interface QNResumeUpload (ALAssetSupport)
@property (nonatomic, strong) ALAsset *asset;

@end

在putALasset方法裏會爲此屬性賦值。
接着寫一個獲取指定offset和length的data的方法

objc- (NSData *)dataFromALAssetAtOffset:(NSInteger)offset size:(NSInteger)size
{
    ALAssetRepresentation *rep = [self.asset defaultRepresentation];
    Byte *buffer = (Byte *)malloc(size);
    NSUInteger buffered = [rep getBytes:buffer fromOffset:offset length:size error:nil];

    return [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];
}

最後在makeBlock和putChunk裏調用此方法便可,以makeBlock爲例

objc- (void)makeBlock:(NSString *)uphost
           offset:(UInt32)offset
        blockSize:(UInt32)blockSize
        chunkSize:(UInt32)chunkSize
         progress:(QNInternalProgressBlock)progressBlock
         complete:(QNCompleteBlock)complete
{
    NSData *data = [self dataFromALAssetAtOffset:offset size:chunkSize];//[self.data subdataWithRange:NSMakeRange(offset, (unsigned int)chunkSize)];
    NSString *url = [[NSString alloc] initWithFormat:@"http://%@/mkblk/%u", uphost, (unsigned int)blockSize];

    UInt32 crc = [QNCrc32 data:data];
    [self setChunkCrcValue:crc];

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
    SEL selector = @selector(post:withData:withCompleteBlock:withProgressBlock:);
#pragma clang diagnostic pop
    void (*typed_msgSend)(id, SEL, NSString *, NSData *, QNCompleteBlock, QNInternalProgressBlock) = (void *)objc_msgSend;
    typed_msgSend(self, selector, url, data, complete, progressBlock);
}

代碼

https://github.com/NSFish/QiNiuALAssetSupport

相關文章
相關標籤/搜索