Full Code of huhuanming/qiniu_upload for AI

master 62f76c55b4a9 cached
55 files
187.6 KB
53.9k tokens
7 symbols
1 requests
Download .txt
Showing preview only (205K chars total). Download the full file or copy to clipboard to get everything.
Repository: huhuanming/qiniu_upload
Branch: master
Commit: 62f76c55b4a9
Files: 55
Total size: 187.6 KB

Directory structure:
gitextract_ev4nzkd6/

├── CHANGELOG.md
├── Classes/
│   ├── POSInputStreamLibrary/
│   │   ├── ALAssetsLibrary+POS.h
│   │   ├── ALAssetsLibrary+POS.m
│   │   ├── NSInputStream+POS.h
│   │   ├── NSInputStream+POS.m
│   │   ├── POSAdjustedAssetReaderIOS7.h
│   │   ├── POSAdjustedAssetReaderIOS7.m
│   │   ├── POSAdjustedAssetReaderIOS8.h
│   │   ├── POSAdjustedAssetReaderIOS8.m
│   │   ├── POSAssetReader.h
│   │   ├── POSBlobInputStream-Prefix.pch
│   │   ├── POSBlobInputStream.h
│   │   ├── POSBlobInputStream.m
│   │   ├── POSBlobInputStreamAssetDataSource.h
│   │   ├── POSBlobInputStreamAssetDataSource.m
│   │   ├── POSBlobInputStreamDataSource.h
│   │   ├── POSFastAssetReader.h
│   │   ├── POSFastAssetReader.m
│   │   ├── POSInputStreamLibrary-Prefix.pch
│   │   ├── POSInputStreamLibrary.h
│   │   ├── POSLocking.h
│   │   └── POSLocking.m
│   ├── QUMBase64.h
│   ├── QUMBase64.m
│   ├── QUMDefines.h
│   ├── QiniuFile.h
│   ├── QiniuFile.m
│   ├── QiniuInputStream.h
│   ├── QiniuInputStream.m
│   ├── QiniuMultipartElement.h
│   ├── QiniuMultipartElement.m
│   ├── QiniuToken.h
│   ├── QiniuToken.m
│   ├── QiniuUploader.h
│   ├── QiniuUploader.m
│   └── version.json
├── LICENSE
├── QiniuUpload.podspec
├── QiniuUploadDemo/
│   ├── .gitignore
│   ├── QiniuUploadDemo/
│   │   ├── AppDelegate.h
│   │   ├── AppDelegate.m
│   │   ├── Assets.xcassets/
│   │   │   └── AppIcon.appiconset/
│   │   │       └── Contents.json
│   │   ├── Base.lproj/
│   │   │   ├── LaunchScreen.storyboard
│   │   │   └── Main.storyboard
│   │   ├── Info.plist
│   │   ├── ViewController.h
│   │   ├── ViewController.m
│   │   └── main.m
│   ├── QiniuUploadDemo.xcodeproj/
│   │   ├── project.pbxproj
│   │   └── project.xcworkspace/
│   │       └── contents.xcworkspacedata
│   ├── QiniuUploadDemoTests/
│   │   ├── Info.plist
│   │   └── QiniuUploadDemoTests.m
│   └── QiniuUploadDemoUITests/
│       ├── Info.plist
│       └── QiniuUploadDemoUITests.m
└── README.md

================================================
FILE CONTENTS
================================================

================================================
FILE: CHANGELOG.md
================================================
# CHANGELOG

## x.x.x Release notes (yyyy-MM-dd)

### API breaking changes

### Enhancements

### Bugfixes

## 3.0.0 Release notes (2017-08-24)

### API breaking changes

* 将 files 从 NSMutableArray 改为 NSArray
* 将 Block 设置移到了 startUpload 函数上
* 移除了直接在 QiniuUploader 上设置 Token
* 添加了并发上传的支持

## 2.0.3 Release notes (2017-07-24)

### Bugfixes

* 修复七牛 URL 失效的问题
* 修复 Markdown 样式失效的问题
* 修复 iOS 10 下选择图片,崩溃的问题

## 2.0.2 Release notes (2016-09-16)

### Enhancements

* error 统一为 NSError 类型
* 七牛上传失败的错误信息都写入到了 error 中,可以对应七牛的失败错误码查看

## 2.0.0 Release notes (2016-09-03)

## API breaking changes

* UploadOneFileSucceededBlock、UploadOneFileFailedBlock、UploadOneFileProgressBlock 参数发生了变化,不再暴露 NSURLSessionTask
* processAssetBlock 已彻底移除, 如果有处理图片的需求,推荐处理完图片后,保存到 temp 文件夹再上传
* 在 QiniuUploader 中新增 @property (assign, atomic)Boolean isRunning 可以检查当前是否正在上传
* 在 QiniuFile 中新增 @property ALAsset *asset 
* 在 QiniuFile 中新增 - (id)initWithPath:(NSString *)path path 是文件路径

### Enhancements

* Xcode 7.3.1 重建了该项目
* 彻底移除了 AFNetworking 的支持
* 支持文件 NSURL 和 ALAsset 从文件流中读取并上传
* 减小了内存占用,清除了可见的内存泄露

## 1.5.4

### Enhancements

* 为 Demo 中新增了价格按钮事件
* 为 GTMBASE64 更换了名字,避免同一工程中,文件名重复

### Bugfixes

* 修复了 cancelAllUploadTask 不能正常工作的问题

## 1.5.2

### Enhancements

* 增加了在开发环境下的版本更新提示
* 依赖的 AFNetworking 换到了 3.0 以上版本
* 修复了所有 warnings

## 1.3

### Enhancements

* 从服务器获取七牛的 token

## 1.2.1

### Bugfixes

* 修复当文件不存在时,引起的崩溃

## 1.2

### Enhancements

* 增强了对 ALAsset URL 的支持

## 1.0.1

大规模重构

## 0.1.1

### Bugfixes

* 修正了 scope 写死的错误

## 0.1

实现了七牛空间的文件上传,和多文件队列上传。


================================================
FILE: Classes/POSInputStreamLibrary/ALAssetsLibrary+POS.h
================================================
//
//  ALAssetsLibrary+POS.h
//  POSInputStreamLibrary
//
//  Created by Osipov on 31.08.15.
//  Copyright (c) 2015 Pavel Osipov. All rights reserved.
//

#import <AssetsLibrary/AssetsLibrary.h>

typedef void (^POSAssetLookupResultBlock)(ALAsset *asset, ALAssetsGroup *assetsGroup);

@interface ALAssetsLibrary (POS)

- (void)pos_assetForURL:(NSURL *)assetURL
            resultBlock:(POSAssetLookupResultBlock)resultBlock
           failureBlock:(ALAssetsLibraryAccessFailureBlock)failureBlock;

@end


================================================
FILE: Classes/POSInputStreamLibrary/ALAssetsLibrary+POS.m
================================================
//
//  ALAssetsLibrary+POS.m
//  POSInputStreamLibrary
//
//  Created by Osipov on 31.08.15.
//  Copyright (c) 2015 Pavel Osipov. All rights reserved.
//

#import "ALAssetsLibrary+POS.h"

@implementation ALAssetsLibrary (POS)

- (void)pos_assetForURL:(NSURL *)assetURL
            resultBlock:(POSAssetLookupResultBlock)resultBlock
           failureBlock:(ALAssetsLibraryAccessFailureBlock)failureBlock {
    [self assetForURL:assetURL resultBlock:^(ALAsset *asset) {
        if (asset) {
            resultBlock(asset, nil);
            return;
        }
        __block BOOL groupAssetFound = NO;
        [self
         enumerateGroupsWithTypes:ALAssetsGroupPhotoStream
         usingBlock:^(ALAssetsGroup *group, BOOL *stopEnumeratingGroups) {
             if (groupAssetFound) {
                 *stopEnumeratingGroups = YES;
                 return;
             }
             if (!group) {
                 resultBlock(nil, nil);
                 return;
             }
             [group enumerateAssetsUsingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stopEnumeratingAssets) {
                 if (!asset) {
                     return;
                 }
                 // For iOS 5 you should use another check:
                 // [[[asset valueForProperty:ALAssetPropertyURLs] allObjects] lastObject]
                 if ([[asset valueForProperty:ALAssetPropertyAssetURL] isEqual:assetURL]) {
                     resultBlock(asset, group);
                     groupAssetFound = YES;
                     *stopEnumeratingAssets = YES;
                 }
             }];
         } failureBlock:failureBlock];
    } failureBlock:failureBlock];
}

@end


================================================
FILE: Classes/POSInputStreamLibrary/NSInputStream+POS.h
================================================
//
//  NSInputStream+POS.h
//  POSBlobInputStreamLibrary
//
//  Created by Pavel Osipov on 17.07.13.
//  Copyright (c) 2013 Pavel Osipov. All rights reserved.
//

#import <AssetsLibrary/AssetsLibrary.h>

@interface NSInputStream (POS)

+ (NSInputStream *)pos_inputStreamWithAssetURL:(NSURL *)assetURL;
+ (NSInputStream *)pos_inputStreamWithAssetURL:(NSURL *)assetURL asynchronous:(BOOL)asynchronous;

+ (NSInputStream *)pos_inputStreamForCFNetworkWithAssetURL:(NSURL *)assetURL;
+ (NSInputStream *)pos_inputStreamForAFNetworkingWithAssetURL:(NSURL *)assetURL;

@end


================================================
FILE: Classes/POSInputStreamLibrary/NSInputStream+POS.m
================================================
//
//  NSInputStream+POS.m
//  POSBlobInputStreamLibrary
//
//  Created by Pavel Osipov on 17.07.13.
//  Copyright (c) 2013 Pavel Osipov. All rights reserved.
//

#import "NSInputStream+POS.h"

#import "POSBlobInputStream.h"
#import "POSBlobInputStreamAssetDataSource.h"

@implementation NSInputStream (POS)

+ (NSInputStream *)pos_inputStreamWithAssetURL:(NSURL *)assetURL {
    return [NSInputStream pos_inputStreamWithAssetURL:assetURL asynchronous:YES];
}

+ (NSInputStream *)pos_inputStreamWithAssetURL:(NSURL *)assetURL asynchronous:(BOOL)asynchronous {
    POSBlobInputStreamAssetDataSource *dataSource = [[POSBlobInputStreamAssetDataSource alloc] initWithAssetURL:assetURL];
    dataSource.openSynchronously = !asynchronous;
    POSBlobInputStream *stream = [[POSBlobInputStream alloc] initWithDataSource:dataSource];
    stream.shouldNotifyCoreFoundationAboutStatusChange = YES;
    return stream;
}

+ (NSInputStream *)pos_inputStreamForCFNetworkWithAssetURL:(NSURL *)assetURL {
    POSBlobInputStreamAssetDataSource *dataSource = [[POSBlobInputStreamAssetDataSource alloc] initWithAssetURL:assetURL];
    dataSource.openSynchronously = YES;
    POSBlobInputStream *stream = [[POSBlobInputStream alloc] initWithDataSource:dataSource];
    stream.shouldNotifyCoreFoundationAboutStatusChange = NO;
    return stream;
}

+ (NSInputStream *)pos_inputStreamForAFNetworkingWithAssetURL:(NSURL *)assetURL {
    POSBlobInputStreamAssetDataSource *dataSource = [[POSBlobInputStreamAssetDataSource alloc] initWithAssetURL:assetURL];
    dataSource.openSynchronously = YES;
    dataSource.openDispatchQueue = POSBlobInputStreamAssetDataSource.sharedOpenDispatchQueue;
    POSBlobInputStream *stream = [[POSBlobInputStream alloc] initWithDataSource:dataSource];
    stream.shouldNotifyCoreFoundationAboutStatusChange = NO;
    return stream;
}

@end


================================================
FILE: Classes/POSInputStreamLibrary/POSAdjustedAssetReaderIOS7.h
================================================
//
//  POSAdjustedAssetReaderIOS7.h
//  POSInputStreamLibrary
//
//  Created by Pavel Osipov on 12.05.15.
//  Copyright (c) 2015 Pavel Osipov. All rights reserved.
//

#import "POSAssetReader.h"

@interface POSAdjustedAssetReaderIOS7 : NSObject <POSAssetReader>

@property (nonatomic, assign) CGFloat JPEGCompressionQuality;

/*!
    @brief Dispatch queue for fetching ALAsset from ALAssetsLibrary.
    @remarks See POSBlobInputStreamAssetDataSource.h
 */
@property (nonatomic, strong) dispatch_queue_t completionDispatchQueue;

@end


================================================
FILE: Classes/POSInputStreamLibrary/POSAdjustedAssetReaderIOS7.m
================================================
//
//  POSAdjustedAssetReaderIOS7.m
//  POSInputStreamLibrary
//
//  Created by Pavel Osipov on 12.05.15.
//  Copyright (c) 2015 Pavel Osipov. All rights reserved.
//

#import "POSAdjustedAssetReaderIOS7.h"

#import <MobileCoreServices/MobileCoreServices.h>
#import <ImageIO/ImageIO.h>
#import <UIKit/UIKit.h>

@interface POSAdjustedAssetReaderIOS7 ()
@property (nonatomic) NSData *imageData;
@end

@implementation POSAdjustedAssetReaderIOS7 {
    NSData *_imageData;
}
@dynamic imageData;

- (instancetype)init {
    if (self = [super init]) {
        _JPEGCompressionQuality = .93f;
    }
    return self;
}

#pragma mark - Properties

- (NSData *)imageData {
    @synchronized(self) {
        return _imageData;
    }
}

- (void)setImageData:(NSData *)imageData {
    @synchronized(self) {
        _imageData = imageData;
    }
}

#pragma mark - POSAssetReader

- (void)openAsset:(ALAssetRepresentation *)assetRepresentation
       fromOffset:(POSLength)offset
completionHandler:(void (^)(POSLength assetSize, NSError *error))completionHandler {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
        NSError *error;
        @autoreleasepool {
            UIImage *image = [self p_adjustedImageDataFromAssetRepresentation:assetRepresentation error:&error];
            if (image) {
                NSData *imageData = [self p_dataFromImage:image withUTI:assetRepresentation.UTI error:&error];
                if (imageData) {
                    self.imageData = [self p_dataForImageData:imageData
                                                 withMetadata:assetRepresentation.metadata
                                                        error:&error];
                }
            }
        }
        dispatch_async(self.completionDispatchQueue ?: dispatch_get_main_queue(), ^{
            completionHandler(self.imageData.length, error);
        });
    });
}

- (BOOL)hasBytesAvailableFromOffset:(POSLength)offset {
    return self.imageData.length - offset > 0;
}

- (BOOL)prepareForNewOffset:(POSLength)offset {
    return YES;
}

- (NSInteger)read:(uint8_t *)buffer
       fromOffset:(POSLength)offset
        maxLength:(NSUInteger)maxLength
            error:(NSError **)error {
    NSData *imageData = self.imageData;
    const POSLength readResult = MIN(maxLength, MAX(imageData.length - offset, 0));
    NSRange dataRange = (NSRange){
        .location = (NSUInteger)offset,
        .length = (NSUInteger)readResult
    };
    [imageData getBytes:buffer range:dataRange];
    return (NSInteger)readResult;
}

#pragma mark - Private

- (NSData *)p_dataForImageData:(NSData *)imageData
                  withMetadata:(NSDictionary *)imageMetadata
                         error:(NSError **)error {
    CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL);
    if (!source) {
        if (error) *error = [NSError errorWithDomain:POSBlobInputStreamAssetDataSourceErrorDomain code:102 userInfo:@{
            NSLocalizedDescriptionKey: @"Failed to init buffer for image."
        }];
        return nil;
    }
    CFStringRef UTI = CGImageSourceGetType(source);
    NSMutableData *data = [NSMutableData data];
    CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)data, UTI, 1, NULL);
    if (!destination) {
        CFRelease(source);
        if (error) *error = [NSError errorWithDomain:POSBlobInputStreamAssetDataSourceErrorDomain code:103 userInfo:@{
            NSLocalizedDescriptionKey: @"Failed to add image data to buffer."
        }];
        return nil;
    }
    CGImageDestinationAddImageFromSource(destination, source, 0, (__bridge CFDictionaryRef)imageMetadata);
    const BOOL finalized = CGImageDestinationFinalize(destination);
    CFRelease(destination);
    CFRelease(source);
    if (!finalized) {
        if (error) *error = [NSError errorWithDomain:POSBlobInputStreamAssetDataSourceErrorDomain code:104 userInfo:@{
            NSLocalizedDescriptionKey: @"Failed to dump image data with metadata to in-memory buffer."
        }];
        return nil;
    }
    return data;
}

- (UIImage *)p_adjustedImageDataFromAssetRepresentation:(ALAssetRepresentation *)assetRepresentation
                                                  error:(NSError **)error {
    NSString *xmpString = assetRepresentation.metadata[@"AdjustmentXMP"];
    NSData *xmpData = [xmpString dataUsingEncoding:NSUTF8StringEncoding];
    CGImageRef fullResolutionImage = [assetRepresentation fullResolutionImage];
    if (!fullResolutionImage) {
        if (error) *error = [NSError errorWithDomain:POSBlobInputStreamAssetDataSourceErrorDomain code:111 userInfo:@{
            NSLocalizedDescriptionKey: @"Failed to get source image for rendering."
        }];
        return nil;
    }
    CIImage *image = [CIImage imageWithCGImage:fullResolutionImage];
    NSArray *filters = [CIFilter filterArrayFromSerializedXMP:xmpData
                                             inputImageExtent:image.extent
                                                        error:error];
    if (*error) {
        return nil;
    }
    for (CIFilter *filter in filters) {
        [filter setValue:image forKey:kCIInputImageKey];
        image = [filter outputImage];
    }
    CIContext *context = [CIContext contextWithOptions:@{ kCIContextUseSoftwareRenderer : @YES }];
    CGImageRef renderedImage = [context createCGImage:image fromRect:[image extent]];
    UIImage *resultImage = [UIImage imageWithCGImage:(renderedImage ? renderedImage : fullResolutionImage)
                                               scale:[assetRepresentation scale]
                                         orientation:(UIImageOrientation)[assetRepresentation orientation]];
    if (renderedImage) {
        CGImageRelease(renderedImage);
    }
    return resultImage;
}

- (NSData *)p_dataFromImage:(UIImage *)image withUTI:(NSString *)UTI error:(NSError **)error {
    NSData *data;
    if (UTTypeConformsTo((__bridge CFStringRef)UTI, kUTTypeJPEG) ||
        UTTypeConformsTo((__bridge CFStringRef)UTI, kUTTypeJPEG2000)) {
        data = UIImageJPEGRepresentation(image, _JPEGCompressionQuality);
    } else {
        data = UIImagePNGRepresentation(image);
    }
    if (!data) {
        if (error) *error = [NSError errorWithDomain:POSBlobInputStreamAssetDataSourceErrorDomain code:121 userInfo:@{
            NSLocalizedDescriptionKey: @"Failed to generate data for image."
        }];
    }
    return data;
}

@end


================================================
FILE: Classes/POSInputStreamLibrary/POSAdjustedAssetReaderIOS8.h
================================================
//
//  POSAdjustedAssetReaderIOS8.h
//  POSInputStreamLibrary
//
//  Created by Pavel Osipov on 12.05.15.
//  Copyright (c) 2015 Pavel Osipov. All rights reserved.
//

#import "POSAssetReader.h"

@interface POSAdjustedAssetReaderIOS8 : NSObject <POSAssetReader>

/// When you try to get adjusted photo just after taking it in Camera app,
/// Photos framework will provide data of 'SubstandardFullSizeRender.jpg'.
/// Asset reader will force Photos framework to generate 'FullSizeRender.jpg'
/// making 2nd attempt to open asset. I think a "suspicious" image size is
/// more adequate parameter to rely on, than the name of the file which you
/// can take from the info dictionary with 'PHImageFileURLKey' key.
@property (nonatomic, assign) long long suspiciousSize;

/*!
    @brief Dispatch queue for fetching ALAsset from ALAssetsLibrary.
    @remarks See POSBlobInputStreamAssetDataSource.h
 */
@property (nonatomic, strong) dispatch_queue_t completionDispatchQueue;

@end


================================================
FILE: Classes/POSInputStreamLibrary/POSAdjustedAssetReaderIOS8.m
================================================
//
//  POSAdjustedAssetReaderIOS8.m
//  POSInputStreamLibrary
//
//  Created by Pavel Osipov on 12.05.15.
//  Copyright (c) 2015 Pavel Osipov. All rights reserved.
//

#import "POSAdjustedAssetReaderIOS8.h"
#import <Photos/Photos.h>

@interface POSAdjustedAssetReaderIOS8 ()
@property (nonatomic) NSData *imageData;
@end

@implementation POSAdjustedAssetReaderIOS8

- (instancetype)init {
    if (self = [super init]) {
        _suspiciousSize = LONG_LONG_MAX;
    }
    return self;
}

#pragma mark - POSAssetReader

- (void)openAsset:(ALAssetRepresentation *)assetRepresentation
       fromOffset:(POSLength)offset
completionHandler:(void (^)(POSLength assetSize, NSError *error))completionHandler {
    NSError *error;
    PHAsset *asset = [self p_fetchAssetForWithURL:assetRepresentation.url error:&error];
    if (!asset) {
        completionHandler(0, error);
        return;
    }
    void (^openCompletionBlock)(NSData *, NSError *) = ^void(NSData *assetData, NSError *error) {
        self.imageData = assetData;
        dispatch_async(self.completionDispatchQueue ?: dispatch_get_main_queue(), ^{
            completionHandler([_imageData length], error);
        });
    };
    [self p_fetchAssetDataForAsset:asset completionBlock:^(NSData *assetData, NSError *error) {
        if ([assetData length] <= _suspiciousSize) {
            [self p_fetchAssetDataForAsset:asset completionBlock:openCompletionBlock];
        } else {
            openCompletionBlock(assetData, error);
        }
    }];
}

- (BOOL)hasBytesAvailableFromOffset:(POSLength)offset {
    return [_imageData length] - offset > 0;;
}

- (BOOL)prepareForNewOffset:(POSLength)offset {
    return YES;
}

- (NSInteger)read:(uint8_t *)buffer
       fromOffset:(POSLength)offset
        maxLength:(NSUInteger)maxLength
            error:(NSError **)error {
    const POSLength readResult = MIN(maxLength, MAX([_imageData length] - offset, 0));
    NSRange dataRange = (NSRange){
        .location = (NSUInteger)offset,
        .length = (NSUInteger)readResult
    };
    [_imageData getBytes:buffer range:dataRange];
    return (NSInteger)readResult;
}

#pragma mark - Private

- (PHAsset *)p_fetchAssetForWithURL:(NSURL *)assetURL error:(NSError **)error {
    PHFetchOptions *options = [PHFetchOptions new];
    options.wantsIncrementalChangeDetails = NO;
    PHFetchResult *assets = [PHAsset fetchAssetsWithALAssetURLs:@[assetURL] options:options];
    if ([assets count] == 0) {
        if (error) *error = [NSError errorWithDomain:POSBlobInputStreamAssetDataSourceErrorDomain
                                                code:201
                                            userInfo:@{ NSLocalizedDescriptionKey: @"Image not found." }];
        return nil;
    }
    return [assets firstObject];
}

- (void)p_fetchAssetDataForAsset:(PHAsset *)asset
                 completionBlock:(void (^)(NSData *assetData, NSError *error))completionHandler {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ @autoreleasepool {
        PHImageManager *imageManager = [PHImageManager defaultManager];
        PHImageRequestOptions *options = [PHImageRequestOptions new];
        options.version = PHImageRequestOptionsVersionCurrent;
        options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
        options.resizeMode = PHImageRequestOptionsResizeModeNone;
        options.synchronous = YES;
        options.networkAccessAllowed = NO;
        [imageManager
         requestImageDataForAsset:asset
         options:options
         resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
            if (info[PHImageErrorKey] != nil) {
                NSError *error = [NSError errorWithDomain:POSBlobInputStreamAssetDataSourceErrorDomain
                                                     code:211
                                                 userInfo:@{ NSLocalizedDescriptionKey: @"Failed to fetch data for image.",
                                                             NSUnderlyingErrorKey: info[PHImageErrorKey]}];
                completionHandler(nil, error);
            } else if ([info[PHImageCancelledKey] boolValue]) {
                NSError *error = [NSError errorWithDomain:POSBlobInputStreamAssetDataSourceErrorDomain
                                                     code:212
                                                 userInfo:@{ NSLocalizedDescriptionKey: @"Fetching data for image was canceled."}];
                completionHandler(nil, error);
            } else if ([info[PHImageResultIsInCloudKey] boolValue]) {
                NSError *error = [NSError errorWithDomain:POSBlobInputStreamAssetDataSourceErrorDomain
                                                     code:213
                                                 userInfo:@{ NSLocalizedDescriptionKey: @"Image is located in the cloud."}];
                completionHandler(nil, error);
            } else {
                completionHandler(imageData, nil);
            }
        }];
    }});
}

@end


================================================
FILE: Classes/POSInputStreamLibrary/POSAssetReader.h
================================================
//
//  POSAssetReader.h
//  POSInputStreamLibrary
//
//  Created by Pavel Osipov on 08.05.15.
//  Copyright (c) 2015 Pavel Osipov. All rights reserved.
//

#import <AssetsLibrary/AssetsLibrary.h>

FOUNDATION_EXTERN NSString * const POSBlobInputStreamAssetDataSourceErrorDomain;

typedef long long POSLength;

@protocol POSAssetReader

- (void)openAsset:(ALAssetRepresentation *)assetRepresentation
       fromOffset:(POSLength)offset
completionHandler:(void (^)(POSLength assetSize, NSError *error))completionHandler;

- (BOOL)hasBytesAvailableFromOffset:(POSLength)offset;

- (BOOL)prepareForNewOffset:(POSLength)offset;

- (NSInteger)read:(uint8_t *)buffer
       fromOffset:(POSLength)offset
        maxLength:(NSUInteger)maxLength
            error:(NSError **)error;

@end


================================================
FILE: Classes/POSInputStreamLibrary/POSBlobInputStream-Prefix.pch
================================================
//
//  Prefix header
//
//  The contents of this file are implicitly included at the beginning of every source file.
//

#ifdef __OBJC__
    #import <Foundation/Foundation.h>
#endif


================================================
FILE: Classes/POSInputStreamLibrary/POSBlobInputStream.h
================================================
//
//  POSBlobInputStream.h
//  POSBlobInputStreamLibrary
//
//  Created by Pavel Osipov on 02.07.13.
//  Copyright (c) 2013 Pavel Osipov. All rights reserved.
//

#import <Foundation/Foundation.h>

FOUNDATION_EXTERN NSString * const POSBlobInputStreamErrorDomain;

typedef NS_ENUM(NSUInteger, POSBlobInputStreamErrorCode) {
    POSBlobInputStreamErrorCodeDataSourceFailure = 0
};

@protocol POSBlobInputStreamDataSource;

@interface POSBlobInputStream : NSInputStream

@property (nonatomic, assign) BOOL shouldNotifyCoreFoundationAboutStatusChange;

// Designated initializer.
- (id)initWithDataSource:(NSObject<POSBlobInputStreamDataSource> *)dataSource;

@end


================================================
FILE: Classes/POSInputStreamLibrary/POSBlobInputStream.m
================================================
//
//  POSBlobInputStream.m
//  POSBlobInputStreamLibrary
//
//  Created by Pavel Osipov on 02.07.13.
//  Copyright (c) 2013 Pavel Osipov. All rights reserved.
//

#import "POSBlobInputStream.h"
#import "POSBlobInputStreamDataSource.h"
#import <objc/runtime.h>

NSString * const POSBlobInputStreamErrorDomain = @"com.github.pavelosipov.POSBlobInputStreamErrorDomain";

NSString * const POSBlobInputStreamDataSourceOpenCompletedKeyPath = @"openCompleted";
NSString * const POSBlobInputStreamDataSourceHasBytesAvailableKeyPath = @"hasBytesAvailable";
NSString * const POSBlobInputStreamDataSourceAtEndKeyPath = @"atEnd";
NSString * const POSBlobInputStreamDataSourceErrorKeyPath = @"error";

static NSInteger const kOperationFailedReturnCode = -1;
static char const POSBlobInputStreamObservingContext;

#pragma mark - Core Foundation callbacks

static const void *POSRetainCallBack(CFAllocatorRef allocator, const void *value) { return CFRetain(value); }
static void POSReleaseCallBack(CFAllocatorRef allocator, const void *value)       { CFRelease(value); }

static void POSRunLoopPerformCallBack(void *info);

#pragma mark - POSBlobInputStream ()

@interface POSBlobInputStream () <NSStreamDelegate> {
    __weak id<NSStreamDelegate> _delegate;
    CFRunLoopSourceRef _runLoopSource;
    NSObject<POSBlobInputStreamDataSource> *_dataSource;
    NSStreamEvent _pendingEvents;
    NSStreamStatus _status;
    NSError *_error;
    NSMutableDictionary *_properties;
    CFReadStreamClientCallBack _clientCallBack;
    CFStreamClientContext _clientContext;
    CFOptionFlags _clientFlags;
    CFMutableSetRef _runLoopsSet;
    CFMutableDictionaryRef _runLoopsModes;
}

@end

#pragma mark - POSBlobInputStream

@implementation POSBlobInputStream

- (id)init {
    @throw [NSException exceptionWithName:NSInternalInconsistencyException
                                   reason:[NSString stringWithFormat:@"Unexpected deadly init invokation '%@', use %@ instead.",
                                           NSStringFromSelector(_cmd),
                                           NSStringFromSelector(@selector(initWithDataSource:))]
                                 userInfo:nil];
}

- (id)initWithDataSource:(NSObject<POSBlobInputStreamDataSource> *)dataSource {
    NSParameterAssert(dataSource);
    if (self = [super init]) {
        _shouldNotifyCoreFoundationAboutStatusChange = NO;
        [self setDataSource:dataSource];
        CFRunLoopSourceContext runLoopSourceContext = {
            0, (__bridge void *)(self), NULL, NULL, NULL, NULL, NULL, NULL, NULL, POSRunLoopPerformCallBack
        };
        _runLoopSource = CFRunLoopSourceCreate(NULL, 0, &runLoopSourceContext);
        _status = NSStreamStatusNotOpen;
        _delegate = self;
        _clientCallBack = NULL;
        _clientContext = (CFStreamClientContext) { 0 };
        CFSetCallBacks runLoopsSetCallBacks = {
            0, NULL, NULL, NULL, CFEqual, CFHash // CFRunLoop retains CFStream, so we will not.
        };
        _runLoopsSet = CFSetCreateMutable(NULL, 0, &runLoopsSetCallBacks);
        CFDictionaryKeyCallBacks runLoopsModesKeyCallBacks = {
            0, NULL, NULL, NULL, CFEqual, CFHash
        };
        CFDictionaryValueCallBacks runLoopsModesValueCallBacks = {
            0, POSRetainCallBack, POSReleaseCallBack, NULL, CFEqual
        };
        _runLoopsModes = CFDictionaryCreateMutable(NULL, 0, &runLoopsModesKeyCallBacks, &runLoopsModesValueCallBacks);
    }
    return self;
}

#pragma mark - NSInputStream

- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)maxLength {
    if (![self isOpen]) {
        NSLog(@"%@: rejected attempt to read stream with status %ld.", self, (long)_status);
        return kOperationFailedReturnCode;
    }
    if (_status == NSStreamStatusAtEnd) {
        return 0;
    }
    const NSInteger readResult = [_dataSource read:buffer maxLength:maxLength];
    if (readResult < 0) {
        return kOperationFailedReturnCode;
    }
    if ([_dataSource hasBytesAvailable]) {
        [self enqueueEvent:NSStreamEventHasBytesAvailable];
    }
    return readResult;
}

- (BOOL)getBuffer:(uint8_t **)buffer length:(NSUInteger *)bufferLength {
    return [_dataSource getBuffer:buffer length:bufferLength];
}

- (BOOL)hasBytesAvailable {
    switch (_status) {
        case NSStreamStatusNotOpen: {
            NSLog(@"%@: you should open stream before fetching for available bytes.", self);
            return NO;
        }
        case NSStreamStatusError: {
            NSLog(@"%@: stream is in error state.", self);
            return NO;
        }
        default: {
            return [_dataSource hasBytesAvailable];
        }
    }
}

#pragma mark - NSStream

- (void)open {
    if (_status != NSStreamStatusNotOpen) {
        NSLog(@"%@: reject attempt to reopen stream.", self);
        return;
    }
    [self setStatus:NSStreamStatusOpening];
    [_dataSource open];
}

- (void)close {
    if (![self isOpen]) {
        return;
    }
    [self unscheduleFromAllRunLoops];
    [self setStatus:NSStreamStatusClosed];
}

- (id<NSStreamDelegate>)delegate {
    return _delegate;
}

- (void)setDelegate:(id<NSStreamDelegate>)delegate {
    _delegate = delegate;
    if (!_delegate) {
        _delegate = self;
    }
}

- (id)propertyForKey:(NSString *)key {
    return [_dataSource propertyForKey:key];
}

- (BOOL)setProperty:(id)property forKey:(NSString *)key {
    return [_dataSource setProperty:property forKey:key];
}

- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode {
    [self scheduleInCFRunLoop:[aRunLoop getCFRunLoop] forMode:(CFStringRef) mode];
}

- (void)removeFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode {
    [self unscheduleFromCFRunLoop:[aRunLoop getCFRunLoop] forMode:(CFStringRef) mode];
}

- (NSStreamStatus)streamStatus {
    if (_status == NSStreamStatusError && !_shouldNotifyCoreFoundationAboutStatusChange) {
        return NSStreamStatusOpen;
    }
    return _status;
}

- (NSError *)streamError {
    NSError *dataSourceError = [_dataSource error];
    if (dataSourceError) {
        return [NSError errorWithDomain:POSBlobInputStreamErrorDomain
                                   code:POSBlobInputStreamErrorCodeDataSourceFailure
                               userInfo:@{ NSUnderlyingErrorKey : dataSourceError }];
    } else {
        return nil;
    }
}

#pragma mark - NSObject

+ (BOOL)resolveInstanceMethod:(SEL)selector {
    NSString *name = NSStringFromSelector(selector);
    if ([name hasPrefix:@"_"]) {
        name = [name substringFromIndex:1];
        SEL aSelector = NSSelectorFromString(name);
        Method method = class_getInstanceMethod(self, aSelector);
        if (method) {
            class_addMethod(self,
                            selector,
                            method_getImplementation(method),
                            method_getTypeEncoding(method));
            return YES;
        }
    }
    return [super resolveInstanceMethod:selector];
}

- (void)dealloc {
    if ([self isOpen]) {
        [self close];
    }
    if (_clientContext.release) {
        _clientContext.release(_clientContext.info);
    }
    CFRelease(_runLoopSource);
    CFRelease(_runLoopsSet);
    CFRelease(_runLoopsModes);
    [_dataSource removeObserver:self forKeyPath:POSBlobInputStreamDataSourceOpenCompletedKeyPath];
    [_dataSource removeObserver:self forKeyPath:POSBlobInputStreamDataSourceHasBytesAvailableKeyPath];
    [_dataSource removeObserver:self forKeyPath:POSBlobInputStreamDataSourceAtEndKeyPath];
    [_dataSource removeObserver:self forKeyPath:POSBlobInputStreamDataSourceErrorKeyPath];
}

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {
    if (context == &POSBlobInputStreamObservingContext) {
        id newValue = [change objectForKey:NSKeyValueChangeNewKey];
        if ([keyPath isEqualToString:POSBlobInputStreamDataSourceOpenCompletedKeyPath] && [newValue boolValue]) {
            [self setStatus:NSStreamStatusOpen];
            [self enqueueEvent:NSStreamEventOpenCompleted];
        } else if ([keyPath isEqualToString:POSBlobInputStreamDataSourceHasBytesAvailableKeyPath] && [newValue boolValue]) {
            [self enqueueEvent:NSStreamEventHasBytesAvailable];
        } else if ([keyPath isEqualToString:POSBlobInputStreamDataSourceAtEndKeyPath] && [newValue boolValue]) {
            [self setStatus:NSStreamStatusAtEnd];
            [self enqueueEvent:NSStreamEventEndEncountered];
        } else if ([keyPath isEqualToString:POSBlobInputStreamDataSourceErrorKeyPath] && newValue != nil) {
            [self setError:newValue];
        }
    } else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

#pragma mark - POSBlobInputStream Private

- (void)setDataSource:(NSObject<POSBlobInputStreamDataSource> *)dataSource {
    _dataSource = dataSource;
    [_dataSource addObserver:self
                  forKeyPath:POSBlobInputStreamDataSourceOpenCompletedKeyPath
                     options:NSKeyValueObservingOptionNew
                     context:(void *)&POSBlobInputStreamObservingContext];
    [_dataSource addObserver:self
                  forKeyPath:POSBlobInputStreamDataSourceHasBytesAvailableKeyPath
                     options:NSKeyValueObservingOptionNew
                     context:(void *)&POSBlobInputStreamObservingContext];
    [_dataSource addObserver:self
                  forKeyPath:POSBlobInputStreamDataSourceAtEndKeyPath
                     options:NSKeyValueObservingOptionNew
                     context:(void *)&POSBlobInputStreamObservingContext];
    [_dataSource addObserver:self
                  forKeyPath:POSBlobInputStreamDataSourceErrorKeyPath
                     options:NSKeyValueObservingOptionNew
                     context:(void *)&POSBlobInputStreamObservingContext];
}

- (BOOL)isOpen {
    return (_status != NSStreamStatusNotOpen &&
            _status != NSStreamStatusOpening &&
            _status != NSStreamStatusClosed &&
            _status != NSStreamStatusError);
    
}

- (void)setStatus:(NSStreamStatus)aStatus {
    _status = aStatus;
}

- (void)setError:(NSError *)theError {
    [self setStatus:NSStreamStatusError];
    [self enqueueEvent:NSStreamEventErrorOccurred];
    _error = theError;
}

- (void)enqueueEvent:(NSStreamEvent)event {
    _pendingEvents |= event;
    CFRunLoopSourceSignal(_runLoopSource);
    [self enumerateRunLoopsUsingBlock:^(CFRunLoopRef runLoop) {
        CFRunLoopWakeUp(runLoop);
    }];
}

- (NSStreamEvent)dequeueEvent {
    if (_pendingEvents == NSStreamEventNone) {
        return NSStreamEventNone;
    }
    NSStreamEvent event = 1UL << __builtin_ctz(_pendingEvents);
    _pendingEvents ^= event;
    return event;
}

- (void)streamEventTrigger {
    if (_status == NSStreamStatusClosed) {
        return;
    }
    NSStreamEvent event = [self dequeueEvent];
    while (event != NSStreamEventNone) {
        if ([_delegate respondsToSelector:@selector(stream:handleEvent:)]) {
            [_delegate stream:self handleEvent:event];
        }
        if (_clientCallBack && (event & _clientFlags) && _shouldNotifyCoreFoundationAboutStatusChange) {
            _clientCallBack((__bridge CFReadStreamRef)self, (CFStreamEventType)event, _clientContext.info);
        }
        event = [self dequeueEvent];
    }
}

- (void)enumerateRunLoopsUsingBlock:(void (^)(CFRunLoopRef runLoop))block {
    CFIndex runLoopsCount = CFSetGetCount(_runLoopsSet);
    if (runLoopsCount > 0) {
        CFTypeRef runLoops[runLoopsCount];
        CFSetGetValues(_runLoopsSet, runLoops);
        for (CFIndex i = 0; i < runLoopsCount; ++i) {
            block((CFRunLoopRef)runLoops[i]);
        }
    }
}

- (void)addMode:(CFStringRef)mode forRunLoop:(CFRunLoopRef)runLoop {
    CFMutableSetRef modes = NULL;
    if (!CFDictionaryContainsKey(_runLoopsModes, runLoop)) {
        CFSetCallBacks modesSetCallBacks = {
            0, POSRetainCallBack, POSReleaseCallBack, NULL, CFEqual, CFHash
        };
        modes = CFSetCreateMutable(NULL, 0, &modesSetCallBacks);
        CFDictionaryAddValue(_runLoopsModes, runLoop, modes);
    } else {
        modes = (CFMutableSetRef)CFDictionaryGetValue(_runLoopsModes, runLoop);
    }
    CFStringRef modeCopy = CFStringCreateCopy(NULL, mode);
    CFSetAddValue(modes, modeCopy);
    CFRelease(modeCopy);
}

- (void)removeMode:(CFStringRef)mode forRunLoop:(CFRunLoopRef)runLoop {
    if (!CFDictionaryContainsKey(_runLoopsModes, runLoop)) {
        return;
    }
    CFMutableSetRef modes = (CFMutableSetRef)CFDictionaryGetValue(_runLoopsModes, runLoop);
    CFSetRemoveValue(modes, mode);
}

- (void)scheduleInCFRunLoop:(CFRunLoopRef)runLoop forMode:(CFStringRef)mode {
    CFSetAddValue(_runLoopsSet, runLoop);
    [self addMode:mode forRunLoop:runLoop];
    CFRunLoopAddSource(runLoop, _runLoopSource, mode);
}

- (void)unscheduleFromCFRunLoop:(CFRunLoopRef)runLoop forMode:(CFStringRef)mode {
    CFRunLoopRemoveSource(runLoop, _runLoopSource, mode);
    [self removeMode:mode forRunLoop:runLoop];
    CFSetRemoveValue(_runLoopsSet, runLoop);
}

- (void)unscheduleFromAllRunLoops {
    [self enumerateRunLoopsUsingBlock:^(CFRunLoopRef runLoop) {
        CFMutableSetRef runLoopModesSet = (CFMutableSetRef)CFDictionaryGetValue(_runLoopsModes, runLoop);
        CFIndex runLoopModesCount = CFSetGetCount(runLoopModesSet);
        if (runLoopModesCount > 0) {
            CFTypeRef runLoopModes[runLoopModesCount];
            CFSetGetValues(runLoopModesSet, runLoopModes);
            for (CFIndex j = 0; j < runLoopModesCount; ++j) {
                [self unscheduleFromCFRunLoop:runLoop forMode:(CFStringRef)runLoopModes[j]];
            }
        }
    }];
}

- (BOOL)setCFClientFlags:(CFOptionFlags)flags
                callback:(CFReadStreamClientCallBack)callBack
                 context:(CFStreamClientContext *)context {
    if (context && context->version != 0) {
        return NO;
    }
    if (_clientContext.release) {
        _clientContext.release(_clientContext.info);
    }
    _clientContext = (CFStreamClientContext) { 0 };
    if (context) {
        _clientContext = *context;
    }
    if (_clientContext.retain) {
        _clientContext.retain(_clientContext.info);
    }
    _clientFlags = flags;
    _clientCallBack = callBack;
    return YES;
}

@end

#pragma mark - Core Foundation callbacks implementations

void POSRunLoopPerformCallBack(void *info) {
    POSBlobInputStream *stream = (__bridge POSBlobInputStream *)info;
    [stream streamEventTrigger];
}


================================================
FILE: Classes/POSInputStreamLibrary/POSBlobInputStreamAssetDataSource.h
================================================
//
//  POSBlobInputStreamAssetDataSource.h
//  POSInputStreamLibrary
//
//  Created by Pavel Osipov on 16.07.13.
//  Copyright (c) 2015 Pavel Osipov. All rights reserved.
//

#import "POSBlobInputStreamDataSource.h"
#import <AssetsLibrary/AssetsLibrary.h>

/// These are the only types of errors which raises POSBlobInputStreamAssetDataSource.
typedef NS_ENUM(NSInteger, POSBlobInputStreamAssetDataSourceErrorCode) {
    POSBlobInputStreamAssetDataSourceErrorCodeOpen = 0,
    POSBlobInputStreamAssetDataSourceErrorCodeRead = 1
};

/// Data source for streaming ALAsset from AssetsLibrary.
@interface POSBlobInputStreamAssetDataSource : NSObject <POSBlobInputStreamDataSource>
/*!
    @brief Indicates that the stream should block calling thread until opening
           will not complete.
 
    @discussion This flag should be YES for streams which are used in NSURLRequest
                or some other synchronous client's code. The only limitation of
                sync mode is that you can not use it while working with a stream
                in the main thread.
 */
@property (nonatomic, assign, getter = shouldOpenSynchronously) BOOL openSynchronously;

/*!
    @brief Value within [0, 1] range which determines compression quality for
           adjusted JPEGs. The default value is 0.93.
 
    @discussion Adjustment filters on iOS7 applied by data source manually using
                hardware acceleration. After applying them on JPEG images
                UIImageJPEGRepresentation function will be used to get raw bytes
                for resulted UIImage. The value of that property will be bypassed
                to it as a second argument.
 */
@property (nonatomic, assign) CGFloat adjustedJPEGCompressionQuality;

/*!
    @brief The suspicious size of assets to detect adjusted photos in iOS 8 gallery.
           The default value is 1M.
 
    @discussion System Camera app has a strange behaviour on iOS 8. If you turn ON
                built-in filters and make a photo with them, then metadata of that
                photo will not have adjustent XML. At the same time the size of the
                asset will be something about 150-300K instead of usual 1.5-3M. This
                property is used for detecting that kind of adjusted images on iOS 8.
                If asset is smaller than adjustedImageMaximumSize value then iOS 8
                Photos framework will be used for reading asset's data. The drawback
                is the app will consume much more RAM, because instead of streaming
                asset directly from ALAssetsLibrary it will allocate RAM for the whole
                UIImage at once.
 
    @remarks See comments in POSAdjustedAssetReaderIOS8.h for more info.
 */
@property (nonatomic, assign) long long adjustedImageMaximumSize;

/*!
    @brief Dispatch queue for fetching ALAsset from ALAssetsLibrary.

    @discussion By default when stream is opened, current dispatch queue is locked and
                ALAsset is retrieved on main dispatch queue. AFNetworking also uses
                main dispatch queue to open NSInputStream so we cannot use it.
 */
@property (nonatomic, strong) dispatch_queue_t openDispatchQueue;

/// The designated initializer.
- (instancetype)initWithAssetURL:(NSURL *)assetURL;

/// Shared queue for fetching ALAssets from ALAssetsLibrary.
+ (dispatch_queue_t)sharedOpenDispatchQueue;

@end


================================================
FILE: Classes/POSInputStreamLibrary/POSBlobInputStreamAssetDataSource.m
================================================
//
//  POSBlobInputStreamAssetDataSource.m
//  POSInputStreamLibrary
//
//  Created by Osipov on 06.05.15.
//  Copyright (c) 2015 Pavel Osipov. All rights reserved.
//

#import "POSBlobInputStreamAssetDataSource.h"
#import "POSFastAssetReader.h"
#import "POSAdjustedAssetReaderIOS7.h"
#import "POSAdjustedAssetReaderIOS8.h"
#import "POSLocking.h"
#import "ALAssetsLibrary+POS.h"

#import <MobileCoreServices/MobileCoreServices.h>
#import <UIKit/UIKit.h>

NSString * const POSBlobInputStreamAssetDataSourceErrorDomain = @"com.github.pavelosipov.POSBlobInputStreamAssetDataSource";

static const char * const POSInputStreamSharedOpenDispatchQueueName = "com.github.pavelosipov.POSInputStreamSharedOpenDispatchQueue";

NSInteger const kPOSReadFailureReturnCode = -1;

typedef NS_ENUM(int, ResetMode) {
    ResetModeReopenWhenError,
    ResetModeFailWhenError
};

@interface NSError (POSBlobInputStreamAssetDataSource)
+ (NSError *)pos_assetOpenErrorWithURL:(NSURL *)assetURL reason:(NSError *)reason;
+ (NSError *)pos_assetReadErrorWithURL:(NSURL *)assetURL reason:(NSError *)reason;
@end

@interface POSBlobInputStreamAssetDataSource ()
@property (nonatomic) NSError *error;
@property (nonatomic) NSURL *assetURL;
@property (nonatomic) ALAssetsLibrary *assetsLibrary;
@property (nonatomic) ALAsset *asset;
@property (nonatomic) ALAssetRepresentation *assetRepresentation;
@property (nonatomic) POSLength assetSize;
@property (nonatomic) id<POSAssetReader> assetReader;
@property (nonatomic) POSLength readOffset;
@end

@implementation POSBlobInputStreamAssetDataSource

@dynamic openCompleted, hasBytesAvailable, atEnd;

#pragma mark - Lifecycle

- (id)init {
    @throw [NSException exceptionWithName:NSInternalInconsistencyException
                                   reason:[NSString stringWithFormat:@"Unexpected deadly init invokation '%@', use %@ instead.",
                                           NSStringFromSelector(_cmd),
                                           NSStringFromSelector(@selector(initWithAssetURL:))]
                                 userInfo:nil];
}

- (instancetype)initWithAssetURL:(NSURL *)assetURL {
    NSParameterAssert(assetURL);
    if (self = [super init]) {
        _openSynchronously = NO;
        _assetURL = assetURL;
        _adjustedJPEGCompressionQuality = .93f;
        _adjustedImageMaximumSize = 1024 * 1024;
    }
    return self;
}

#pragma mark - POSBlobInputStreamDataSource

+ (dispatch_queue_t)sharedOpenDispatchQueue {
    static dispatch_queue_t queue = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        queue = dispatch_queue_create(POSInputStreamSharedOpenDispatchQueueName, NULL);
    });
    return queue;
}

- (BOOL)isOpenCompleted {
    return _assetSize > 0;
}

- (void)open {
    if (![self isOpenCompleted]) {
        [self p_open];
    }
}

- (void)setAssetSize:(POSLength)assetSize {
    const BOOL shouldEmitOpenCompletedEvent = ![self isOpenCompleted];
    if (shouldEmitOpenCompletedEvent) {
        [self willChangeValueForKey:POSBlobInputStreamDataSourceOpenCompletedKeyPath];
    }
    _assetSize = assetSize;
    if (shouldEmitOpenCompletedEvent) {
        [self didChangeValueForKey:POSBlobInputStreamDataSourceOpenCompletedKeyPath];
    }
}

- (BOOL)hasBytesAvailable {
    return [_assetReader hasBytesAvailableFromOffset:_readOffset];
}

- (BOOL)isAtEnd {
    return _assetSize <= _readOffset;
}

- (id)propertyForKey:(NSString *)key {
    if (![key isEqualToString:NSStreamFileCurrentOffsetKey]) {
        return nil;
    }
    return @(_readOffset);
}

- (BOOL)setProperty:(id)property forKey:(NSString *)key {
    if (![key isEqualToString:NSStreamFileCurrentOffsetKey]) {
        return NO;
    }
    if (![property isKindOfClass:[NSNumber class]]) {
        return NO;
    }
    const long long requestedOffest = [property longLongValue];
    if (requestedOffest < 0) {
        return NO;
    }
    _readOffset = requestedOffest;
    if (_assetReader) {
        return [_assetReader prepareForNewOffset:_readOffset];
    }
    return YES;
}

- (BOOL)getBuffer:(uint8_t **)buffer length:(NSUInteger *)bufferLength {
    return NO;
}

- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)maxLength {
    NSParameterAssert(buffer);
    NSParameterAssert(maxLength > 0);
    if (self.atEnd) {
        return 0;
    }
    NSError *error;
    const POSLength readResult = [_assetReader read:buffer
                                         fromOffset:_readOffset
                                          maxLength:maxLength
                                              error:&error];
    const POSLength readOffset = _readOffset + readResult;
    NSParameterAssert(readOffset <= _assetSize);
    const BOOL atEnd = readOffset >= _assetSize;
    if (atEnd) {
        [self willChangeValueForKey:POSBlobInputStreamDataSourceAtEndKeyPath];
    }
    _readOffset = readOffset;
    if (atEnd) {
        [self didChangeValueForKey:POSBlobInputStreamDataSourceAtEndKeyPath];
    } else if (error) {
        [self p_open];
    }
    return (NSInteger)readResult;
}

#pragma mark - Private

- (void)p_open {
    id<POSLocking> lock = [self p_lockForOpening];
    [lock lock];
    dispatch_async(self.openDispatchQueue ?: dispatch_get_main_queue(), ^{ @autoreleasepool {
        self.assetsLibrary = [ALAssetsLibrary new];
        [_assetsLibrary pos_assetForURL:_assetURL resultBlock:^(ALAsset *asset, ALAssetsGroup *assetsGroup) {
            ALAssetRepresentation *assetRepresentation = [asset defaultRepresentation];
            if (assetRepresentation) {
                self.asset = asset;
                self.assetRepresentation = assetRepresentation;
                self.assetReader = (assetsGroup
                                    ? [POSFastAssetReader new]
                                    : [self p_assetReaderForAssetRepresentation:assetRepresentation]);
                [_assetReader
                 openAsset:assetRepresentation
                 fromOffset:_readOffset
                 completionHandler:^(POSLength assetSize, NSError *error) {
                     if (error != nil || assetSize <= 0 || (_assetSize != 0 && _assetSize != assetSize)) {
                         self.error = [NSError pos_assetOpenErrorWithURL:_assetURL reason:error];
                     } else {
                         self.assetSize = assetSize;
                     }
                     [lock unlock];
                 }];
            } else {
                self.error = [NSError pos_assetOpenErrorWithURL:_assetURL reason:nil];
                [lock unlock];
            }
        } failureBlock:^(NSError *error) {
            self.error = [NSError pos_assetOpenErrorWithURL:_assetURL reason:error];
            [lock unlock];
        }];
    }});
    [lock waitWithTimeout:DISPATCH_TIME_FOREVER];
}

- (id<POSAssetReader>)p_assetReaderForAssetRepresentation:(ALAssetRepresentation *)representation {
    if (_assetReader) {
        return _assetReader;
    }
    if (!UTTypeConformsTo((__bridge CFStringRef)representation.UTI, kUTTypeImage)) {
        return [POSFastAssetReader new];
    }
    if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0 &&
        representation.size <= _adjustedImageMaximumSize) {
        POSAdjustedAssetReaderIOS8 *assetReader = [POSAdjustedAssetReaderIOS8 new];
        assetReader.suspiciousSize = _adjustedImageMaximumSize;
        assetReader.completionDispatchQueue = self.openDispatchQueue;
        return assetReader;
    }
    if (representation.metadata[@"AdjustmentXMP"] != nil) {
        POSAdjustedAssetReaderIOS7 *assetReader = [POSAdjustedAssetReaderIOS7 new];
        assetReader.JPEGCompressionQuality = _adjustedJPEGCompressionQuality;
        assetReader.completionDispatchQueue = self.openDispatchQueue;
        return assetReader;
    }
    return [POSFastAssetReader new];
}

- (id<POSLocking>)p_lockForOpening {
    if ([self shouldOpenSynchronously]) {
        if (!self.openDispatchQueue) {
            // If you want open stream synchronously you should
            // do that in some worker thread to avoid deadlock.
            NSParameterAssert(![[NSThread currentThread] isMainThread]);
        }
        return [POSGCDLock new];
    } else {
        return [POSDummyLock new];
    }
}

@end

@implementation NSError (POSBlobInputStreamAssetDataSource)

+ (NSError *)pos_assetOpenErrorWithURL:(NSURL *)assetURL reason:(NSError *)reason {
    NSString *description = [NSString stringWithFormat:@"Failed to open asset with URL %@", assetURL];
    if (reason) {
        return [NSError errorWithDomain:POSBlobInputStreamAssetDataSourceErrorDomain
                                   code:POSBlobInputStreamAssetDataSourceErrorCodeOpen
                               userInfo:@{ NSLocalizedDescriptionKey: description, NSUnderlyingErrorKey: reason }];
    } else {
        return [NSError errorWithDomain:POSBlobInputStreamAssetDataSourceErrorDomain
                                   code:POSBlobInputStreamAssetDataSourceErrorCodeOpen
                               userInfo:@{ NSLocalizedDescriptionKey: description }];
    }
}

+ (NSError *)pos_assetReadErrorWithURL:(NSURL *)assetURL reason:(NSError *)reason {
    NSString *description = [NSString stringWithFormat:@"Failed to read asset with URL %@", assetURL];
    if (reason) {
        return [NSError errorWithDomain:POSBlobInputStreamAssetDataSourceErrorDomain
                                   code:POSBlobInputStreamAssetDataSourceErrorCodeRead
                               userInfo:@{ NSLocalizedDescriptionKey: description, NSUnderlyingErrorKey: reason }];
    } else {
        return [NSError errorWithDomain:POSBlobInputStreamAssetDataSourceErrorDomain
                                   code:POSBlobInputStreamAssetDataSourceErrorCodeRead
                               userInfo:@{ NSLocalizedDescriptionKey: description }];
    }
}

@end


================================================
FILE: Classes/POSInputStreamLibrary/POSBlobInputStreamDataSource.h
================================================
//
//  POSBlobInputStreamDataSource.h
//  POSBlobInputStreamLibrary
//
//  Created by Pavel Osipov on 16.07.13.
//  Copyright (c) 2013 Pavel Osipov. All rights reserved.
//

#import <Foundation/Foundation.h>

FOUNDATION_EXTERN NSString * const POSBlobInputStreamDataSourceOpenCompletedKeyPath;
FOUNDATION_EXTERN NSString * const POSBlobInputStreamDataSourceHasBytesAvailableKeyPath;
FOUNDATION_EXTERN NSString * const POSBlobInputStreamDataSourceAtEndKeyPath;
FOUNDATION_EXTERN NSString * const POSBlobInputStreamDataSourceErrorKeyPath;

@protocol POSBlobInputStreamDataSource <NSObject>

//
// Self-explanatory KVO-compliant properties.
//
@property (nonatomic, readonly, getter = isOpenCompleted) BOOL openCompleted;
@property (nonatomic, readonly) BOOL hasBytesAvailable;
@property (nonatomic, readonly, getter = isAtEnd) BOOL atEnd;
@property (nonatomic, readonly) NSError *error;

//
// This selector will be called before anything else.
//
- (void)open;

//
// Data Source configuring.
//
- (id)propertyForKey:(NSString *)key;
- (BOOL)setProperty:(id)property forKey:(NSString *)key;

//
// Data Source data.
// The contracts of these selectors are the same as for NSInputStream.
//
- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)maxLength;
- (BOOL)getBuffer:(uint8_t **)buffer length:(NSUInteger *)bufferLength;

@end


================================================
FILE: Classes/POSInputStreamLibrary/POSFastAssetReader.h
================================================
//
//  POSFastAssetReader.h
//  POSInputStreamLibrary
//
//  Created by Pavel Osipov on 08.05.15.
//  Copyright (c) 2015 Pavel Osipov. All rights reserved.
//

#import "POSAssetReader.h"

@interface POSFastAssetReader : NSObject <POSAssetReader>

@end


================================================
FILE: Classes/POSInputStreamLibrary/POSFastAssetReader.m
================================================
//
//  POSFastAssetReader.m
//  POSInputStreamLibrary
//
//  Created by Pavel Osipov on 08.05.15.
//  Copyright (c) 2015 Pavel Osipov. All rights reserved.
//

#import "POSFastAssetReader.h"

static uint64_t const kAssetCacheBufferSize = 131072;

@implementation POSFastAssetReader {
    uint8_t _assetCache[kAssetCacheBufferSize];
    POSLength _assetSize;
    POSLength _assetCacheSize;
    POSLength _assetCacheOffset;
    POSLength _assetCacheInternalOffset;
    ALAssetRepresentation *_assetRepresentation;
}

#pragma mark - POSAssetReader

- (void)openAsset:(ALAssetRepresentation *)assetRepresentation
       fromOffset:(POSLength)offset
completionHandler:(void (^)(POSLength, NSError *))completionHandler {
    _assetRepresentation = assetRepresentation;
    NSError *error;
    [self p_refillCacheFromOffset:offset error:&error];
    completionHandler(_assetSize, error);
}

- (BOOL)hasBytesAvailableFromOffset:(POSLength)offset {
    if ([self p_cachedBytesCount] <= 0) {
        return NO;
    }
    return offset < _assetCacheOffset + _assetCacheSize;
}

- (BOOL)prepareForNewOffset:(POSLength)offset {
    return [self p_refillCacheFromOffset:offset error:nil];
}

- (NSInteger)read:(uint8_t *)buffer
       fromOffset:(POSLength)offset
        maxLength:(NSUInteger)maxLength
            error:(NSError **)error {
    const POSLength readResult = MIN(maxLength, [self p_cachedBytesCount]);
    memcpy(buffer, _assetCache + _assetCacheInternalOffset, (unsigned long)readResult);
    _assetCacheInternalOffset += readResult;
    const POSLength nextReadOffset = offset + readResult;
    if ([self p_cachedBytesCount] <= 0 ||
        [self p_unreadBytesCountFromOffset:nextReadOffset] > 0) {
        [self p_refillCacheFromOffset:nextReadOffset error:error];
    }
    return (NSInteger)readResult;
}

#pragma mark - Private

- (POSLength)p_unreadBytesCountFromOffset:(POSLength)offset {
    return _assetSize - offset;
}

- (POSLength)p_cachedBytesCount {
    return _assetCacheSize - _assetCacheInternalOffset;
}

- (BOOL)p_refillCacheFromOffset:(POSLength)offset error:(NSError **)error {
    const NSUInteger readResult = [_assetRepresentation getBytes:_assetCache
                                                      fromOffset:offset
                                                          length:kAssetCacheBufferSize
                                                           error:error];
    if (readResult <= 0) {
        if (error) {
            NSString *desc = [NSString stringWithFormat:@"Failed to read asset bytes in range %@ from asset of size %@.",
                              NSStringFromRange(NSMakeRange((NSUInteger)offset, (NSUInteger)kAssetCacheBufferSize)),
                              @(_assetSize)];
            *error = [NSError errorWithDomain:POSBlobInputStreamAssetDataSourceErrorDomain
                                         code:-2000
                                     userInfo:@{NSLocalizedDescriptionKey: desc}];
        }
        return NO;
    }
    _assetSize = [_assetRepresentation size];
    _assetCacheSize = readResult;
    _assetCacheOffset = offset;
    _assetCacheInternalOffset = 0;
    return YES;
}

@end


================================================
FILE: Classes/POSInputStreamLibrary/POSInputStreamLibrary-Prefix.pch
================================================
//
//  Prefix header
//
//  The contents of this file are implicitly included at the beginning of every source file.
//

#ifdef __OBJC__
    #import <Foundation/Foundation.h>
#endif


================================================
FILE: Classes/POSInputStreamLibrary/POSInputStreamLibrary.h
================================================
//
//  POSInputStreamLibrary.h
//  POSInputStreamLibrary
//
//  Created by Osipov on 27.02.14.
//  Copyright (c) 2014 Pavel Osipov. All rights reserved.
//

#import "POSBlobInputStream.h"
#import "POSBlobInputStreamAssetDataSource.h"

#import "NSInputStream+POS.h"


================================================
FILE: Classes/POSInputStreamLibrary/POSLocking.h
================================================
//
//  POSLocking.h
//  POSInputStreamLibrary
//
//  Created by Osipov on 06.05.15.
//  Copyright (c) 2015 Pavel Osipov. All rights reserved.
//

#import <Foundation/Foundation.h>

@protocol POSLocking <NSLocking>
- (BOOL)waitWithTimeout:(dispatch_time_t)timeout;
@end

@interface POSGCDLock : NSObject <POSLocking>
@end

@interface POSDummyLock : NSObject <POSLocking>
@end


================================================
FILE: Classes/POSInputStreamLibrary/POSLocking.m
================================================
//
//  POSLocking.m
//  POSInputStreamLibrary
//
//  Created by Osipov on 06.05.15.
//  Copyright (c) 2015 Pavel Osipov. All rights reserved.
//

#import "POSLocking.h"

@implementation POSGCDLock {
    dispatch_semaphore_t semaphore_;
}

- (void)lock {
    semaphore_ = dispatch_semaphore_create(0);
}

- (void)unlock {
    dispatch_semaphore_signal(semaphore_);
}

- (BOOL)waitWithTimeout:(dispatch_time_t)timeout {
    return dispatch_semaphore_wait(semaphore_, timeout) == 0;
}

@end

@implementation POSDummyLock
- (void)lock {}
- (void)unlock {}
- (BOOL)waitWithTimeout:(dispatch_time_t)timeout { return YES; }
@end


================================================
FILE: Classes/QUMBase64.h
================================================
//
//  QUMBase64.h
//  Moto
//
//  Created by 胡 桓铭 on 14-2-17.
//  Copyright (c) 2014年 agile. All rights reserved.
//

//
//  QUMBase64.h
//
//  Copyright 2006-2008 Google Inc.
//
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
//  use this file except in compliance with the License.  You may obtain a copy
//  of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
//  License for the specific language governing permissions and limitations under
//  the License.
//

#import <Foundation/Foundation.h>
#import "QUMDefines.h"

// QUMBase64
//
/// Helper for handling Base64 and WebSafeBase64 encodings
//
/// The webSafe methods use different character set and also the results aren't
/// always padded to a multiple of 4 characters.  This is done so the resulting
/// data can be used in urls and url query arguments without needing any
/// encoding.  You must use the webSafe* methods together, the data does not
/// interop with the RFC methods.
//
@interface QUMBase64 : NSObject

//
// Standard Base64 (RFC) handling
//

// encodeData:
//
/// Base64 encodes contents of the NSData object.
//
/// Returns:
///   A new autoreleased NSData with the encoded payload.  nil for any error.
//
+(NSData *)encodeData:(NSData *)data;

// decodeData:
//
/// Base64 decodes contents of the NSData object.
//
/// Returns:
///   A new autoreleased NSData with the decoded payload.  nil for any error.
//
+(NSData *)decodeData:(NSData *)data;

// encodeBytes:length:
//
/// Base64 encodes the data pointed at by |bytes|.
//
/// Returns:
///   A new autoreleased NSData with the encoded payload.  nil for any error.
//
+(NSData *)encodeBytes:(const void *)bytes length:(NSUInteger)length;

// decodeBytes:length:
//
/// Base64 decodes the data pointed at by |bytes|.
//
/// Returns:
///   A new autoreleased NSData with the encoded payload.  nil for any error.
//
+(NSData *)decodeBytes:(const void *)bytes length:(NSUInteger)length;

// stringByEncodingData:
//
/// Base64 encodes contents of the NSData object.
//
/// Returns:
///   A new autoreleased NSString with the encoded payload.  nil for any error.
//
+(NSString *)stringByEncodingData:(NSData *)data;

// stringByEncodingBytes:length:
//
/// Base64 encodes the data pointed at by |bytes|.
//
/// Returns:
///   A new autoreleased NSString with the encoded payload.  nil for any error.
//
+(NSString *)stringByEncodingBytes:(const void *)bytes length:(NSUInteger)length;

// decodeString:
//
/// Base64 decodes contents of the NSString.
//
/// Returns:
///   A new autoreleased NSData with the decoded payload.  nil for any error.
//
+(NSData *)decodeString:(NSString *)string;

//
// Modified Base64 encoding so the results can go onto urls.
//
// The changes are in the characters generated and also allows the result to
// not be padded to a multiple of 4.
// Must use the matching call to encode/decode, won't interop with the
// RFC versions.
//

// webSafeEncodeData:padded:
//
/// WebSafe Base64 encodes contents of the NSData object.  If |padded| is YES
/// then padding characters are added so the result length is a multiple of 4.
//
/// Returns:
///   A new autoreleased NSData with the encoded payload.  nil for any error.
//
+(NSData *)webSafeEncodeData:(NSData *)data
                      padded:(BOOL)padded;

// webSafeDecodeData:
//
/// WebSafe Base64 decodes contents of the NSData object.
//
/// Returns:
///   A new autoreleased NSData with the decoded payload.  nil for any error.
//
+(NSData *)webSafeDecodeData:(NSData *)data;

// webSafeEncodeBytes:length:padded:
//
/// WebSafe Base64 encodes the data pointed at by |bytes|.  If |padded| is YES
/// then padding characters are added so the result length is a multiple of 4.
//
/// Returns:
///   A new autoreleased NSData with the encoded payload.  nil for any error.
//
+(NSData *)webSafeEncodeBytes:(const void *)bytes
                       length:(NSUInteger)length
                       padded:(BOOL)padded;

// webSafeDecodeBytes:length:
//
/// WebSafe Base64 decodes the data pointed at by |bytes|.
//
/// Returns:
///   A new autoreleased NSData with the encoded payload.  nil for any error.
//
+(NSData *)webSafeDecodeBytes:(const void *)bytes length:(NSUInteger)length;

// stringByWebSafeEncodingData:padded:
//
/// WebSafe Base64 encodes contents of the NSData object.  If |padded| is YES
/// then padding characters are added so the result length is a multiple of 4.
//
/// Returns:
///   A new autoreleased NSString with the encoded payload.  nil for any error.
//
+(NSString *)stringByWebSafeEncodingData:(NSData *)data
                                  padded:(BOOL)padded;

// stringByWebSafeEncodingBytes:length:padded:
//
/// WebSafe Base64 encodes the data pointed at by |bytes|.  If |padded| is YES
/// then padding characters are added so the result length is a multiple of 4.
//
/// Returns:
///   A new autoreleased NSString with the encoded payload.  nil for any error.
//
+(NSString *)stringByWebSafeEncodingBytes:(const void *)bytes
                                   length:(NSUInteger)length
                                   padded:(BOOL)padded;

// webSafeDecodeString:
//
/// WebSafe Base64 decodes contents of the NSString.
//
/// Returns:
///   A new autoreleased NSData with the decoded payload.  nil for any error.
//
+(NSData *)webSafeDecodeString:(NSString *)string;

@end



================================================
FILE: Classes/QUMBase64.m
================================================
//
//  QUMBase64.m
//  Moto
//
//  Created by 胡 桓铭 on 14-2-17.
//  Copyright (c) 2014年 agile. All rights reserved.
//

//
//  QUMBase64.m
//
//  Copyright 2006-2008 Google Inc.
//
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
//  use this file except in compliance with the License.  You may obtain a copy
//  of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
//  License for the specific language governing permissions and limitations under
//  the License.
//

#import "QUMBase64.h"
#import "QUMDefines.h"

static const char *kBase64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const char *kWebSafeBase64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
static const char kBase64PaddingChar = '=';
static const char kBase64InvalidChar = 99;

static const char kBase64DecodeChars[] = {
    // This array was generated by the following code:
    // #include <sys/time.h>
    // #include <stdlib.h>
    // #include <string.h>
    // main()
    // {
    //   static const char Base64[] =
    //     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    //   char *pos;
    //   int idx, i, j;
    //   printf("    ");
    //   for (i = 0; i < 255; i += 8) {
    //     for (j = i; j < i + 8; j++) {
    //       pos = strchr(Base64, j);
    //       if ((pos == NULL) || (j == 0))
    //         idx = 99;
    //       else
    //         idx = pos - Base64;
    //       if (idx == 99)
    //         printf(" %2d,     ", idx);
    //       else
    //         printf(" %2d/*%c*/,", idx, j);
    //     }
    //     printf("\n    ");
    //   }
    // }
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      62/*+*/, 99,      99,      99,      63/*/ */,
    52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/,
    60/*8*/, 61/*9*/, 99,      99,      99,      99,      99,      99,
    99,       0/*A*/,  1/*B*/,  2/*C*/,  3/*D*/,  4/*E*/,  5/*F*/,  6/*G*/,
    7/*H*/,  8/*I*/,  9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/,
    15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/,
    23/*X*/, 24/*Y*/, 25/*Z*/, 99,      99,      99,      99,      99,
    99,      26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/,
    33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/,
    41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/,
    49/*x*/, 50/*y*/, 51/*z*/, 99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99
};

static const char kWebSafeBase64DecodeChars[] = {
    // This array was generated by the following code:
    // #include <sys/time.h>
    // #include <stdlib.h>
    // #include <string.h>
    // main()
    // {
    //   static const char Base64[] =
    //     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
    //   char *pos;
    //   int idx, i, j;
    //   printf("    ");
    //   for (i = 0; i < 255; i += 8) {
    //     for (j = i; j < i + 8; j++) {
    //       pos = strchr(Base64, j);
    //       if ((pos == NULL) || (j == 0))
    //         idx = 99;
    //       else
    //         idx = pos - Base64;
    //       if (idx == 99)
    //         printf(" %2d,     ", idx);
    //       else
    //         printf(" %2d/*%c*/,", idx, j);
    //     }
    //     printf("\n    ");
    //   }
    // }
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      62/*-*/, 99,      99,
    52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/,
    60/*8*/, 61/*9*/, 99,      99,      99,      99,      99,      99,
    99,       0/*A*/,  1/*B*/,  2/*C*/,  3/*D*/,  4/*E*/,  5/*F*/,  6/*G*/,
    7/*H*/,  8/*I*/,  9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/,
    15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/,
    23/*X*/, 24/*Y*/, 25/*Z*/, 99,      99,      99,      99,      63/*_*/,
    99,      26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/,
    33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/,
    41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/,
    49/*x*/, 50/*y*/, 51/*z*/, 99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99,
    99,      99,      99,      99,      99,      99,      99,      99
};


// Tests a character to see if it's a whitespace character.
//
// Returns:
//   YES if the character is a whitespace character.
//   NO if the character is not a whitespace character.
//
QUM_INLINE BOOL IsSpace(unsigned char c) {
    // we use our own mapping here because we don't want anything w/ locale
    // support.
    static BOOL kSpaces[256] = {
        0, 0, 0, 0, 0, 0, 0, 0, 0, 1,  // 0-9
        1, 1, 1, 1, 0, 0, 0, 0, 0, 0,  // 10-19
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 20-29
        0, 0, 1, 0, 0, 0, 0, 0, 0, 0,  // 30-39
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 40-49
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 50-59
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 60-69
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 70-79
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 80-89
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 90-99
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 100-109
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 110-119
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 120-129
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 130-139
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 140-149
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 150-159
        1, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 160-169
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 170-179
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 180-189
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 190-199
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 200-209
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 210-219
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 220-229
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 230-239
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 240-249
        0, 0, 0, 0, 0, 1,              // 250-255
    };
    return kSpaces[c];
}

// Calculate how long the data will be once it's base64 encoded.
//
// Returns:
//   The guessed encoded length for a source length
//
QUM_INLINE NSUInteger CalcEncodedLength(NSUInteger srcLen, BOOL padded) {
    NSUInteger intermediate_result = 8 * srcLen + 5;
    NSUInteger len = intermediate_result / 6;
    if (padded) {
        len = ((len + 3) / 4) * 4;
    }
    return len;
}

// Tries to calculate how long the data will be once it's base64 decoded.
// Unlike the above, this is always an upperbound, since the source data
// could have spaces and might end with the padding characters on them.
//
// Returns:
//   The guessed decoded length for a source length
//
QUM_INLINE NSUInteger GuessDecodedLength(NSUInteger srcLen) {
    return (srcLen + 3) / 4 * 3;
}


@interface QUMBase64 (PrivateMethods)

+(NSData *)baseEncode:(const void *)bytes
               length:(NSUInteger)length
              charset:(const char *)charset
               padded:(BOOL)padded;

+(NSData *)baseDecode:(const void *)bytes
               length:(NSUInteger)length
              charset:(const char*)charset
       requirePadding:(BOOL)requirePadding;

+(NSUInteger)baseEncode:(const char *)srcBytes
                 srcLen:(NSUInteger)srcLen
              destBytes:(char *)destBytes
                destLen:(NSUInteger)destLen
                charset:(const char *)charset
                 padded:(BOOL)padded;

+(NSUInteger)baseDecode:(const char *)srcBytes
                 srcLen:(NSUInteger)srcLen
              destBytes:(char *)destBytes
                destLen:(NSUInteger)destLen
                charset:(const char *)charset
         requirePadding:(BOOL)requirePadding;

@end


@implementation QUMBase64

//
// Standard Base64 (RFC) handling
//

+(NSData *)encodeData:(NSData *)data {
    return [self baseEncode:[data bytes]
                     length:[data length]
                    charset:kBase64EncodeChars
                     padded:YES];
}

+(NSData *)decodeData:(NSData *)data {
    return [self baseDecode:[data bytes]
                     length:[data length]
                    charset:kBase64DecodeChars
             requirePadding:YES];
}

+(NSData *)encodeBytes:(const void *)bytes length:(NSUInteger)length {
    return [self baseEncode:bytes
                     length:length
                    charset:kBase64EncodeChars
                     padded:YES];
}

+(NSData *)decodeBytes:(const void *)bytes length:(NSUInteger)length {
    return [self baseDecode:bytes
                     length:length
                    charset:kBase64DecodeChars
             requirePadding:YES];
}

+(NSString *)stringByEncodingData:(NSData *)data {
    NSString *result = nil;
    NSData *converted = [self baseEncode:[data bytes]
                                  length:[data length]
                                 charset:kBase64EncodeChars
                                  padded:YES];
    if (converted) {
        result = [[NSString alloc] initWithData:converted
                                        encoding:NSASCIIStringEncoding];
    }
    return result;
}

+(NSString *)stringByEncodingBytes:(const void *)bytes length:(NSUInteger)length {
    NSString *result = nil;
    NSData *converted = [self baseEncode:bytes
                                  length:length
                                 charset:kBase64EncodeChars
                                  padded:YES];
    if (converted) {
        result = [[NSString alloc] initWithData:converted
                                        encoding:NSASCIIStringEncoding];
    }
    return result;
}

+(NSData *)decodeString:(NSString *)string {
    NSData *result = nil;
    NSData *data = [string dataUsingEncoding:NSASCIIStringEncoding];
    if (data) {
        result = [self baseDecode:[data bytes]
                           length:[data length]
                          charset:kBase64DecodeChars
                   requirePadding:YES];
    }
    return result;
}

//
// Modified Base64 encoding so the results can go onto urls.
//
// The changes are in the characters generated and also the result isn't
// padded to a multiple of 4.
// Must use the matching call to encode/decode, won't interop with the
// RFC versions.
//

+(NSData *)webSafeEncodeData:(NSData *)data
                      padded:(BOOL)padded {
    return [self baseEncode:[data bytes]
                     length:[data length]
                    charset:kWebSafeBase64EncodeChars
                     padded:padded];
}

+(NSData *)webSafeDecodeData:(NSData *)data {
    return [self baseDecode:[data bytes]
                     length:[data length]
                    charset:kWebSafeBase64DecodeChars
             requirePadding:NO];
}

+(NSData *)webSafeEncodeBytes:(const void *)bytes
                       length:(NSUInteger)length
                       padded:(BOOL)padded {
    return [self baseEncode:bytes
                     length:length
                    charset:kWebSafeBase64EncodeChars
                     padded:padded];
}

+(NSData *)webSafeDecodeBytes:(const void *)bytes length:(NSUInteger)length {
    return [self baseDecode:bytes
                     length:length
                    charset:kWebSafeBase64DecodeChars
             requirePadding:NO];
}

+(NSString *)stringByWebSafeEncodingData:(NSData *)data
                                  padded:(BOOL)padded {
    NSString *result = nil;
    NSData *converted = [self baseEncode:[data bytes]
                                  length:[data length]
                                 charset:kWebSafeBase64EncodeChars
                                  padded:padded];
    if (converted) {
        result = [[NSString alloc] initWithData:converted
                                        encoding:NSASCIIStringEncoding];
    }
    return result;
}

+(NSString *)stringByWebSafeEncodingBytes:(const void *)bytes
                                   length:(NSUInteger)length
                                   padded:(BOOL)padded {
    NSString *result = nil;
    NSData *converted = [self baseEncode:bytes
                                  length:length
                                 charset:kWebSafeBase64EncodeChars
                                  padded:padded];
    if (converted) {
        result = [[NSString alloc] initWithData:converted
                                        encoding:NSASCIIStringEncoding];
    }
    return result;
}

+(NSData *)webSafeDecodeString:(NSString *)string {
    NSData *result = nil;
    NSData *data = [string dataUsingEncoding:NSASCIIStringEncoding];
    if (data) {
        result = [self baseDecode:[data bytes]
                           length:[data length]
                          charset:kWebSafeBase64DecodeChars
                   requirePadding:NO];
    }
    return result;
}

@end

@implementation QUMBase64 (PrivateMethods)

//
// baseEncode:length:charset:padded:
//
// Does the common lifting of creating the dest NSData.  it creates & sizes the
// data for the results.  |charset| is the characters to use for the encoding
// of the data.  |padding| controls if the encoded data should be padded to a
// multiple of 4.
//
// Returns:
//   an autorelease NSData with the encoded data, nil if any error.
//
+(NSData *)baseEncode:(const void *)bytes
               length:(NSUInteger)length
              charset:(const char *)charset
               padded:(BOOL)padded {
    // how big could it be?
    NSUInteger maxLength = CalcEncodedLength(length, padded);
    // make space
    NSMutableData *result = [NSMutableData data];
    [result setLength:maxLength];
    // do it
    NSUInteger finalLength = [self baseEncode:bytes
                                       srcLen:length
                                    destBytes:[result mutableBytes]
                                      destLen:[result length]
                                      charset:charset
                                       padded:padded];
    if (finalLength) {
        _GTMDevAssert(finalLength == maxLength, @"how did we calc the length wrong?");
    } else {
        // shouldn't happen, this means we ran out of space
        result = nil;
    }
    return result;
}

//
// baseDecode:length:charset:requirePadding:
//
// Does the common lifting of creating the dest NSData.  it creates & sizes the
// data for the results.  |charset| is the characters to use for the decoding
// of the data.
//
// Returns:
//   an autorelease NSData with the decoded data, nil if any error.
//
//
+(NSData *)baseDecode:(const void *)bytes
               length:(NSUInteger)length
              charset:(const char *)charset
       requirePadding:(BOOL)requirePadding {
    // could try to calculate what it will end up as
    NSUInteger maxLength = GuessDecodedLength(length);
    // make space
    NSMutableData *result = [NSMutableData data];
    [result setLength:maxLength];
    // do it
    NSUInteger finalLength = [self baseDecode:bytes
                                       srcLen:length
                                    destBytes:[result mutableBytes]
                                      destLen:[result length]
                                      charset:charset
                               requirePadding:requirePadding];
    if (finalLength) {
        if (finalLength != maxLength) {
            // resize down to how big it was
            [result setLength:finalLength];
        }
    } else {
        // either an error in the args, or we ran out of space
        result = nil;
    }
    return result;
}

//
// baseEncode:srcLen:destBytes:destLen:charset:padded:
//
// Encodes the buffer into the larger.  returns the length of the encoded
// data, or zero for an error.
// |charset| is the characters to use for the encoding
// |padded| tells if the result should be padded to a multiple of 4.
//
// Returns:
//   the length of the encoded data.  zero if any error.
//
+(NSUInteger)baseEncode:(const char *)srcBytes
                 srcLen:(NSUInteger)srcLen
              destBytes:(char *)destBytes
                destLen:(NSUInteger)destLen
                charset:(const char *)charset
                 padded:(BOOL)padded {
    if (!srcLen || !destLen || !srcBytes || !destBytes) {
        return 0;
    }
    
    char *curDest = destBytes;
    const unsigned char *curSrc = (const unsigned char *)(srcBytes);
    
    // Three bytes of data encodes to four characters of cyphertext.
    // So we can pump through three-byte chunks atomically.
    while (srcLen > 2) {
        // space?
        _GTMDevAssert(destLen >= 4, @"our calc for encoded length was wrong");
        curDest[0] = charset[curSrc[0] >> 2];
        curDest[1] = charset[((curSrc[0] & 0x03) << 4) + (curSrc[1] >> 4)];
        curDest[2] = charset[((curSrc[1] & 0x0f) << 2) + (curSrc[2] >> 6)];
        curDest[3] = charset[curSrc[2] & 0x3f];
        
        curDest += 4;
        curSrc += 3;
        srcLen -= 3;
        destLen -= 4;
    }
    
    // now deal with the tail (<=2 bytes)
    switch (srcLen) {
        case 0:
            // Nothing left; nothing more to do.
            break;
        case 1:
            // One byte left: this encodes to two characters, and (optionally)
            // two pad characters to round out the four-character cypherblock.
            _GTMDevAssert(destLen >= 2, @"our calc for encoded length was wrong");
            curDest[0] = charset[curSrc[0] >> 2];
            curDest[1] = charset[(curSrc[0] & 0x03) << 4];
            curDest += 2;
            destLen -= 2;
            if (padded) {
                _GTMDevAssert(destLen >= 2, @"our calc for encoded length was wrong");
                curDest[0] = kBase64PaddingChar;
                curDest[1] = kBase64PaddingChar;
                curDest += 2;
            }
            break;
        case 2:
            // Two bytes left: this encodes to three characters, and (optionally)
            // one pad character to round out the four-character cypherblock.
            _GTMDevAssert(destLen >= 3, @"our calc for encoded length was wrong");
            curDest[0] = charset[curSrc[0] >> 2];
            curDest[1] = charset[((curSrc[0] & 0x03) << 4) + (curSrc[1] >> 4)];
            curDest[2] = charset[(curSrc[1] & 0x0f) << 2];
            curDest += 3;
            destLen -= 3;
            if (padded) {
                _GTMDevAssert(destLen >= 1, @"our calc for encoded length was wrong");
                curDest[0] = kBase64PaddingChar;
                curDest += 1;
            }
            break;
    }
    // return the length
    return (curDest - destBytes);
}

//
// baseDecode:srcLen:destBytes:destLen:charset:requirePadding:
//
// Decodes the buffer into the larger.  returns the length of the decoded
// data, or zero for an error.
// |charset| is the character decoding buffer to use
//
// Returns:
//   the length of the encoded data.  zero if any error.
//
+(NSUInteger)baseDecode:(const char *)srcBytes
                 srcLen:(NSUInteger)srcLen
              destBytes:(char *)destBytes
                destLen:(NSUInteger)destLen
                charset:(const char *)charset
         requirePadding:(BOOL)requirePadding {
    if (!srcLen || !destLen || !srcBytes || !destBytes) {
        return 0;
    }
    
    int decode;
    NSUInteger destIndex = 0;
    int state = 0;
    char ch = 0;
    while (srcLen-- && (ch = *srcBytes++) != 0)  {
        if (IsSpace(ch))  // Skip whitespace
            continue;
        
        if (ch == kBase64PaddingChar)
            break;
        
        decode = charset[(unsigned int)ch];
        if (decode == kBase64InvalidChar)
            return 0;
        
        // Four cyphertext characters decode to three bytes.
        // Therefore we can be in one of four states.
        switch (state) {
            case 0:
                // We're at the beginning of a four-character cyphertext block.
                // This sets the high six bits of the first byte of the
                // plaintext block.
                _GTMDevAssert(destIndex < destLen, @"our calc for decoded length was wrong");
                destBytes[destIndex] = decode << 2;
                state = 1;
                break;
            case 1:
                // We're one character into a four-character cyphertext block.
                // This sets the low two bits of the first plaintext byte,
                // and the high four bits of the second plaintext byte.
                _GTMDevAssert((destIndex+1) < destLen, @"our calc for decoded length was wrong");
                destBytes[destIndex] |= decode >> 4;
                destBytes[destIndex+1] = (decode & 0x0f) << 4;
                destIndex++;
                state = 2;
                break;
            case 2:
                // We're two characters into a four-character cyphertext block.
                // This sets the low four bits of the second plaintext
                // byte, and the high two bits of the third plaintext byte.
                // However, if this is the end of data, and those two
                // bits are zero, it could be that those two bits are
                // leftovers from the encoding of data that had a length
                // of two mod three.
                _GTMDevAssert((destIndex+1) < destLen, @"our calc for decoded length was wrong");
                destBytes[destIndex] |= decode >> 2;
                destBytes[destIndex+1] = (decode & 0x03) << 6;
                destIndex++;
                state = 3;
                break;
            case 3:
                // We're at the last character of a four-character cyphertext block.
                // This sets the low six bits of the third plaintext byte.
                _GTMDevAssert(destIndex < destLen, @"our calc for decoded length was wrong");
                destBytes[destIndex] |= decode;
                destIndex++;
                state = 0;
                break;
        }
    }
    
    // We are done decoding Base-64 chars.  Let's see if we ended
    //      on a byte boundary, and/or with erroneous trailing characters.
    if (ch == kBase64PaddingChar) {               // We got a pad char
        if ((state == 0) || (state == 1)) {
            return 0;  // Invalid '=' in first or second position
        }
        if (srcLen == 0) {
            if (state == 2) { // We run out of input but we still need another '='
                return 0;
            }
            // Otherwise, we are in state 3 and only need this '='
        } else {
            if (state == 2) {  // need another '='
                while ((ch = *srcBytes++) && (srcLen-- > 0)) {
                    if (!IsSpace(ch))
                        break;
                }
                if (ch != kBase64PaddingChar) {
                    return 0;
                }
            }
            // state = 1 or 2, check if all remain padding is space
            while ((ch = *srcBytes++) && (srcLen-- > 0)) {
                if (!IsSpace(ch)) {
                    return 0;
                }
            }
        }
    } else {
        // We ended by seeing the end of the string.
        
        if (requirePadding) {
            // If we require padding, then anything but state 0 is an error.
            if (state != 0) {
                return 0;
            }
        } else {
            // Make sure we have no partial bytes lying around.  Note that we do not
            // require trailing '=', so states 2 and 3 are okay too.
            if (state == 1) {
                return 0;
            }
        }
    }
    
    // If then next piece of output was valid and got written to it means we got a
    // very carefully crafted input that appeared valid but contains some trailing
    // bits past the real length, so just toss the thing.
    if ((destIndex < destLen) &&
        (destBytes[destIndex] != 0)) {
        return 0;
    }
    
    return destIndex;
}

@end


================================================
FILE: Classes/QUMDefines.h
================================================
//
// QUMDefines.h
//
//  Copyright 2008 Google Inc.
//
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
//  use this file except in compliance with the License.  You may obtain a copy
//  of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
//  License for the specific language governing permissions and limitations under
//  the License.
//

// ============================================================================

#include <AvailabilityMacros.h>

// Not all MAC_OS_X_VERSION_10_X macros defined in past SDKs
#ifndef MAC_OS_X_VERSION_10_5
#define MAC_OS_X_VERSION_10_5 1050
#endif
#ifndef MAC_OS_X_VERSION_10_6
#define MAC_OS_X_VERSION_10_6 1060
#endif

// ----------------------------------------------------------------------------
// CPP symbols that can be overridden in a prefix to control how the toolbox
// is compiled.
// ----------------------------------------------------------------------------


// QUMHTTPFetcher will support logging by default but only hook its input
// stream support for logging when requested.  You can control the inclusion of
// the code by providing your own definitions for these w/in a prefix header.
//
#ifndef QUM_HTTPFETCHER_ENABLE_LOGGING
#define QUM_HTTPFETCHER_ENABLE_LOGGING 1
#endif // QUM_HTTPFETCHER_ENABLE_LOGGING
#ifndef QUM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING
#define QUM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING 0
#endif // QUM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING

// By setting the QUM_CONTAINERS_VALIDATION_FAILED_LOG and
// QUM_CONTAINERS_VALIDATION_FAILED_ASSERT macros you can control what happens
// when a validation fails. If you implement your own validators, you may want
// to control their internals using the same macros for consistency.
#ifndef QUM_CONTAINERS_VALIDATION_FAILED_ASSERT
#define QUM_CONTAINERS_VALIDATION_FAILED_ASSERT 0
#endif

// Give ourselves a consistent way to do inlines.  Apple's macros even use
// a few different actual definitions, so we're based off of the foundation
// one.
#if !defined(QUM_INLINE)
#if defined (__GNUC__) && (__GNUC__ == 4)
#define QUM_INLINE static __inline__ __attribute__((always_inline))
#else
#define QUM_INLINE static __inline__
#endif
#endif

// Give ourselves a consistent way of doing externs that links up nicely
// when mixing objc and objc++
#if !defined (QUM_EXTERN)
#if defined __cplusplus
#define QUM_EXTERN extern "C"
#else
#define QUM_EXTERN extern
#endif
#endif

// _GTMDevLog & _GTMDevAssert
//
// _GTMDevLog & _GTMDevAssert are meant to be a very lightweight shell for
// developer level errors.  This implementation simply macros to NSLog/NSAssert.
// It is not intended to be a general logging/reporting system.
//
// Please see http://code.google.com/p/google-toolbox-for-mac/wiki/DevLogNAssert
// for a little more background on the usage of these macros.
//
//    _GTMDevLog           log some error/problem in debug builds
//    _GTMDevAssert        assert if conditon isn't met w/in a method/function
//                           in all builds.
//
// To replace this system, just provide different macro definitions in your
// prefix header.  Remember, any implementation you provide *must* be thread
// safe since this could be called by anything in what ever situtation it has
// been placed in.
//

// We only define the simple macros if nothing else has defined this.
#ifndef _GTMDevLog

#ifdef DEBUG
#define _GTMDevLog(...) NSLog(__VA_ARGS__)
#else
#define _GTMDevLog(...) do { } while (0)
#endif

#endif // _GTMDevLog

// Declared here so that it can easily be used for logging tracking if
// necessary. See QUMUnitTestDevLog.h for details.
@class NSString;
QUM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);

#ifndef _GTMDevAssert
// we directly invoke the NSAssert handler so we can pass on the varargs
// (NSAssert doesn't have a macro we can use that takes varargs)
#if !defined(NS_BLOCK_ASSERTIONS)
#define _GTMDevAssert(condition, ...)                                    \
do {                                                                   \
if (!(condition)) {                                                  \
[[NSAssertionHandler currentHandler]                               \
handleFailureInFunction:[NSString stringWithCString:__PRETTY_FUNCTION__ encoding:NSASCIIStringEncoding] \
file:[NSString stringWithCString:__FILE__ encoding:NSASCIIStringEncoding]  \
lineNumber:__LINE__                               \
description:__VA_ARGS__];                          \
}                                                                    \
} while(0)
#else // !defined(NS_BLOCK_ASSERTIONS)
#define _GTMDevAssert(condition, ...) do { } while (0)
#endif // !defined(NS_BLOCK_ASSERTIONS)

#endif // _GTMDevAssert

// _GTMCompileAssert
// _GTMCompileAssert is an assert that is meant to fire at compile time if you
// want to check things at compile instead of runtime. For example if you
// want to check that a wchar is 4 bytes instead of 2 you would use
// _GTMCompileAssert(sizeof(wchar_t) == 4, wchar_t_is_4_bytes_on_OS_X)
// Note that the second "arg" is not in quotes, and must be a valid processor
// symbol in it's own right (no spaces, punctuation etc).

// Wrapping this in an #ifndef allows external groups to define their own
// compile time assert scheme.
#ifndef _GTMCompileAssert
// We got this technique from here:
// http://unixjunkie.blogspot.com/2007/10/better-compile-time-asserts_29.html

#define _GTMCompileAssertSymbolInner(line, msg) _GTMCOMPILEASSERT ## line ## __ ## msg
#define _GTMCompileAssertSymbol(line, msg) _GTMCompileAssertSymbolInner(line, msg)
#define _GTMCompileAssert(test, msg) \
typedef char _GTMCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ]
#endif // _GTMCompileAssert

// ============================================================================

// ----------------------------------------------------------------------------
// CPP symbols defined based on the project settings so the QUM code has
// simple things to test against w/o scattering the knowledge of project
// setting through all the code.
// ----------------------------------------------------------------------------

// Provide a single constant CPP symbol that all of QUM uses for ifdefing
// iPhone code.
#include <TargetConditionals.h>
#if TARGET_OS_IPHONE // iPhone SDK
// For iPhone specific stuff
#define QUM_IPHONE_SDK 1
#if TARGET_IPHONE_SIMULATOR
#define QUM_IPHONE_SIMULATOR 1
#else
#define QUM_IPHONE_DEVICE 1
#endif  // TARGET_IPHONE_SIMULATOR
#else
// For MacOS specific stuff
#define QUM_MACOS_SDK 1
#endif

// To simplify support for 64bit (and Leopard in general), we provide the type
// defines for non Leopard SDKs
#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
// NSInteger/NSUInteger and Max/Mins
#ifndef NSINTEGER_DEFINED
#if __LP64__ || NS_BUILD_32_LIKE_64
typedef long NSInteger;
typedef unsigned long NSUInteger;
#else
typedef int NSInteger;
typedef unsigned int NSUInteger;
#endif
#define NSIntegerMax    LONG_MAX
#define NSIntegerMin    LONG_MIN
#define NSUIntegerMax   ULONG_MAX
#define NSINTEGER_DEFINED 1
#endif  // NSINTEGER_DEFINED
// CGFloat
#ifndef CGFLOAT_DEFINED
#if defined(__LP64__) && __LP64__
// This really is an untested path (64bit on Tiger?)
typedef double CGFloat;
#define CGFLOAT_MIN DBL_MIN
#define CGFLOAT_MAX DBL_MAX
#define CGFLOAT_IS_DOUBLE 1
#else /* !defined(__LP64__) || !__LP64__ */
typedef float CGFloat;
#define CGFLOAT_MIN FLT_MIN
#define CGFLOAT_MAX FLT_MAX
#define CGFLOAT_IS_DOUBLE 0
#endif /* !defined(__LP64__) || !__LP64__ */
#define CGFLOAT_DEFINED 1
#endif // CGFLOAT_DEFINED
#endif  // MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4


================================================
FILE: Classes/QiniuFile.h
================================================
//
//  QiniuFile.h
//  QiniuUploadDemo
//
//  Created by 胡 桓铭 on 14-5-18.
//  Copyright (c) 2014年 hu. All rights reserved.
//

#import <Foundation/Foundation.h>

#if TARGET_OS_IOS
#import <AssetsLibrary/AssetsLibrary.h>
#endif

@interface QiniuFile : NSObject


#if TARGET_OS_IOS
@property ALAsset *asset;
#endif

/**
 *  name of this file. It's could be nil.
 */
@property (copy, nonatomic) NSString *key;


/**
 *  Default value is @"image/jpeg"
 */
@property (copy, nonatomic) NSString *mimeType;

@property (copy, nonatomic) NSString *path;

@property (copy, nonatomic) NSData *rawData;


/**
 *  initialize instance with file path.
 *  @param path file path
 */
- (id)initWithPath:(NSString *)path;


/**
 *  initialize instance with binary data, and key name for it.
 *  @param theAsset the alasset for ios native resource
 */

#if TARGET_OS_IOS
- (id)initWithAsset:(ALAsset *)asset;
#endif

/**
 *  (only support image)initialize instance with binary data, and key name for it.
 *  @param theData binary data
 */
- (id)initWithFileData:(NSData *)theData;


/**
 *  initialize instance with binary data.
 *  @param theData binary data
 *  @param key name of this binary data
 */
- (id)initWithFileData:(NSData *)theData withKey:(NSString*)theKey;



@end


================================================
FILE: Classes/QiniuFile.m
================================================
//
//  QiniuFile.m
//  QiniuUploadDemo
//
//  Created by 胡 桓铭 on 14-5-18.
//  Copyright (c) 2014年 hu. All rights reserved.
//

#import "QiniuFile.h"

@implementation QiniuFile

- (id)initWithPath:(NSString *)path
{
    if (self = [super init]) {
        self.path = path;
    }
    return self;
}

#if TARGET_OS_IOS
- (id)initWithAsset:(ALAsset *)asset
{
    if (self = [super init]) {
        self.asset = asset;
    }
    return self;
}
#endif

- (id)initWithFileData:(NSData *)theData
{
    return [self initWithFileData:theData withKey:nil];
}

- (id)initWithFileData:(NSData *)theData withKey:(NSString*)theKey
{
    if (self = [super init]) {
        self.key = theKey;
        self.rawData = theData;
        self.mimeType = @"image/jpeg";
    }
    return self;
}

@end


================================================
FILE: Classes/QiniuInputStream.h
================================================
//
//  QiniuInputStream.h
//  QiniuUploadDemo
//
//  Created by 胡 桓铭 on 16/9/3.
//  Copyright © 2016年 胡 桓铭. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "QiniuMultipartElement.h"

@interface QiniuInputStream : NSInputStream
@property (nonatomic, readonly) NSString *boundary;
@property (nonatomic, readonly) NSUInteger length;

- (void)addPartWithName:(NSString *)name string:(NSString *)string;
- (void)addPartWithName:(NSString *)name data:(NSData *)data;
- (void)addPartWithName:(NSString *)name data:(NSData *)data contentType:(NSString *)type;
- (void)addPartWithName:(NSString *)name filename:(NSString*)filename data:(NSData *)data contentType:(NSString *)type;
- (void)addPartWithName:(NSString *)name path:(NSString *)path;
#if TARGET_OS_IOS
- (void)addPartWithName:(NSString *)name asset:(ALAsset *)asset;
#endif
- (void)addPartWithName:(NSString *)name filename:(NSString *)filename path:(NSString *)path;
- (void)addPartWithName:(NSString *)name filename:(NSString *)filename stream:(NSInputStream *)stream streamLength:(NSUInteger)streamLength;
- (void)addPartWithHeaders:(NSDictionary *)headers string:(NSString *)string;
- (void)addPartWithHeaders:(NSDictionary *)headers path:(NSString *)path;

@end


================================================
FILE: Classes/QiniuInputStream.m
================================================
//
//  QiniuInputStream.m
//  QiniuUploadDemo
//
//  Created by 胡 桓铭 on 16/9/3.
//  Copyright © 2016年 胡 桓铭. All rights reserved.
//

#import "QiniuInputStream.h"

@interface QiniuInputStream()
@property (nonatomic, strong) NSMutableArray *parts;
@property (nonatomic, strong) NSString *boundary;
@property (nonatomic, strong) NSData *footer;
@property (nonatomic) NSUInteger currentPart, delivered, length;
@property (nonatomic) NSStreamStatus status;
@end

@implementation QiniuInputStream
@synthesize delegate;

- (void)updateLength
{
    self.length = self.footer.length + [[self.parts valueForKeyPath:@"@sum.length"] unsignedIntegerValue];
}
- (id)init
{
    self = [super init];
    if (self)
    {
        self.parts    = [NSMutableArray array];
        self.boundary = [[NSProcessInfo processInfo] globallyUniqueString];
        self.footer   = [[NSString stringWithFormat:kFooterFormat, self.boundary] dataUsingEncoding:NSUTF8StringEncoding];
        [self updateLength];
    }
    return self;
}
- (void)addPartWithName:(NSString *)name string:(NSString *)string
{
    [self.parts addObject:[[QiniuMultipartElement alloc] initWithName:name boundary:self.boundary string:string]];
    [self updateLength];
}
- (void)addPartWithName:(NSString *)name data:(NSData *)data
{
    [self.parts addObject:[[QiniuMultipartElement alloc] initWithName:name boundary:self.boundary data:data contentType:@"application/octet-stream"]];
    [self updateLength];
}
- (void)addPartWithName:(NSString *)name data:(NSData *)data contentType:(NSString *)type
{
    [self.parts addObject:[[QiniuMultipartElement alloc] initWithName:name boundary:self.boundary data:data contentType:type]];
    [self updateLength];
}
- (void)addPartWithName:(NSString *)name filename:(NSString*)filename data:(NSData *)data contentType:(NSString *)type
{
    [self.parts addObject:[[QiniuMultipartElement alloc] initWithName:name boundary:self.boundary data:data contentType:type filename:filename]];
    [self updateLength];
}
- (void)addPartWithName:(NSString *)name path:(NSString *)path
{
    [self.parts addObject:[[QiniuMultipartElement alloc] initWithName:name filename:nil boundary:self.boundary path:path]];
    [self updateLength];
}
- (void)addPartWithName:(NSString *)name filename:(NSString *)filename path:(NSString *)path
{
    [self.parts addObject:[[QiniuMultipartElement alloc] initWithName:name filename:filename boundary:self.boundary path:path]];
    [self updateLength];
}
- (void)addPartWithName:(NSString *)name filename:(NSString *)filename stream:(NSInputStream *)stream streamLength:(NSUInteger)streamLength
{
    [self.parts addObject:[[QiniuMultipartElement alloc] initWithName:name filename:filename boundary:self.boundary stream:stream streamLength:streamLength]];
    [self updateLength];
}

#if TARGET_OS_IOS
- (void)addPartWithName:(NSString *)name asset:(ALAsset *)asset;
{
    [self.parts addObject:[[QiniuMultipartElement alloc] initWithName:name filename:nil boundary:self.boundary asset:asset]];
    [self updateLength];
}
#endif

- (void)addPartWithHeaders:(NSDictionary *)headers string:(NSString *)string
{
    [self.parts addObject:[[QiniuMultipartElement alloc] initWithHeaders:headers string:string boundary:self.boundary]];
    [self updateLength];
}

- (void)addPartWithHeaders:(NSDictionary *)headers path:(NSString *)path
{
    [self.parts addObject:[[QiniuMultipartElement alloc] initWithHeaders:headers path:path boundary:self.boundary]];
    [self updateLength];
}

- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len
{
    NSUInteger sent = 0, read;
    
    self.status = NSStreamStatusReading;
    while (self.delivered < self.length && sent < len && self.currentPart < self.parts.count)
    {
        if ((read = [[self.parts objectAtIndex:self.currentPart] read:(buffer + sent) maxLength:(len - sent)]) == 0)
        {
            self.currentPart ++;
            continue;
        }
        sent            += read;
        self.delivered  += read;
    }
    if (self.delivered >= (self.length - self.footer.length) && sent < len)
    {
        read            = MIN(self.footer.length - (self.delivered - (self.length - self.footer.length)), len - sent);
        [self.footer getBytes:buffer + sent range:NSMakeRange(self.delivered - (self.length - self.footer.length), read)];
        sent           += read;
        self.delivered += read;
    }
    return sent;
}
- (BOOL)hasBytesAvailable
{
    return self.delivered < self.length;
}
- (void)open
{
    self.status = NSStreamStatusOpen;
}
- (void)close
{
    self.status = NSStreamStatusClosed;
}
- (NSStreamStatus)streamStatus
{
    if (self.status != NSStreamStatusClosed && self.delivered >= self.length)
    {
        self.status = NSStreamStatusAtEnd;
    }
    return self.status;
}
- (void)_scheduleInCFRunLoop:(NSRunLoop *)runLoop forMode:(id)mode {}
- (void)_setCFClientFlags:(CFOptionFlags)flags callback:(CFReadStreamClientCallBack)callback context:(CFStreamClientContext)context {}
- (void)removeFromRunLoop:(__unused NSRunLoop *)aRunLoop forMode:(__unused NSString *)mode {}
@end


================================================
FILE: Classes/QiniuMultipartElement.h
================================================
//
//  QiniuMultipartElement.h
//  QiniuUploadDemo
//
//  Created by 胡 桓铭 on 16/9/3.
//  Copyright © 2016年 胡 桓铭. All rights reserved.
//

#import <Foundation/Foundation.h>

#if TARGET_OS_IOS
#import <MobileCoreServices/UTType.h>
#import "NSInputStream+POS.h"
#endif

#define kHeaderStringFormat @"--%@\r\nContent-Disposition: form-data; name=\"%@\"\r\n\r\n"
#define kHeaderDataFormat @"--%@\r\nContent-Disposition: form-data; name=\"%@\"\r\nContent-Type: %@\r\n\r\n"
#define kHeaderPathFormat @"--%@\r\nContent-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\nContent-Type: %@\r\n\r\n"
#define kFooterFormat @"--%@--\r\n"

@interface QiniuMultipartElement : NSObject
@property (nonatomic, strong) NSData *headers;
@property (nonatomic, strong) NSInputStream *body;
@property (nonatomic) NSUInteger headersLength, bodyLength, length, delivered;

- (id)initWithName:(NSString *)name boundary:(NSString *)boundary string:(NSString *)string;

- (id)initWithName:(NSString *)name boundary:(NSString *)boundary data:(NSData *)data contentType:(NSString *)contentType;

- (id)initWithName:(NSString *)name boundary:(NSString *)boundary data:(NSData *)data contentType:(NSString *)contentType filename:(NSString*)filename;

- (id)initWithName:(NSString *)name filename:(NSString *)filename boundary:(NSString *)boundary path:(NSString *)path;

- (id)initWithName:(NSString *)name filename:(NSString *)filename boundary:(NSString *)boundary stream:(NSInputStream *)stream streamLength:(NSUInteger)streamLength;

- (id)initWithHeaders:(NSDictionary *)headers string:(NSString *)string boundary:(NSString *)boundary;

- (id)initWithHeaders:(NSDictionary *)headers path:(NSString *)path boundary:(NSString *)boundary;

#if TARGET_OS_IOS
- (id)initWithName:(NSString *)name filename:(NSString *)filename boundary:(NSString *)boundary asset:(ALAsset *)asset;
#endif

@end



================================================
FILE: Classes/QiniuMultipartElement.m
================================================
//
//  QiniuMultipartElement.m
//  QiniuUploadDemo
//
//  Created by 胡 桓铭 on 16/9/3.
//  Copyright © 2016年 胡 桓铭. All rights reserved.
//

#import "QiniuMultipartElement.h"

static NSString * MIMETypeForExtension(NSString * extension) {
    
    CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL);
    if (uti != NULL)
    {
        CFStringRef mime = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType);
        CFRelease(uti);
        if (mime != NULL)
        {
            NSString *type = [NSString stringWithString:(__bridge NSString *)mime];
            CFRelease(mime);
            return type;
        }
    }
    return @"application/octet-stream";
}

@implementation QiniuMultipartElement
- (void)updateLength
{
    self.length = self.headersLength + self.bodyLength + 2;
    [self.body open];
}
- (id)initWithName:(NSString *)name boundary:(NSString *)boundary string:(NSString *)string
{
    self               = [super init];
    self.headers       = [[NSString stringWithFormat:kHeaderStringFormat, boundary, name] dataUsingEncoding:NSUTF8StringEncoding];
    self.headersLength = [self.headers length];
    NSData *stringData = [string dataUsingEncoding:NSUTF8StringEncoding];
    self.body          = [NSInputStream inputStreamWithData:stringData];
    self.bodyLength    = stringData.length;
    [self updateLength];
    return self;
}
- (id)initWithName:(NSString *)name boundary:(NSString *)boundary data:(NSData *)data contentType:(NSString *)contentType
{
    self               = [super init];
    self.headers       = [[NSString stringWithFormat:kHeaderDataFormat, boundary, name, contentType] dataUsingEncoding:NSUTF8StringEncoding];
    self.headersLength = [self.headers length];
    self.body          = [NSInputStream inputStreamWithData:data];
    self.bodyLength    = [data length];
    [self updateLength];
    return self;
}
- (id)initWithName:(NSString *)name boundary:(NSString *)boundary data:(NSData *)data contentType:(NSString *)contentType filename:(NSString*)filename
{
    self               = [super init];
    self.headers       = [[NSString stringWithFormat:kHeaderPathFormat, boundary, name, filename, contentType] dataUsingEncoding:NSUTF8StringEncoding];
    self.headersLength = [self.headers length];
    self.body          = [NSInputStream inputStreamWithData:data];
    self.bodyLength    = [data length];
    [self updateLength];
    return self;
}
- (id)initWithName:(NSString *)name filename:(NSString *)filename boundary:(NSString *)boundary path:(NSString *)path
{
    if (!filename)
    {
        filename = path.lastPathComponent;
    }
    self.headers       = [[NSString stringWithFormat:kHeaderPathFormat, boundary, name, filename, MIMETypeForExtension(path.pathExtension)] dataUsingEncoding:NSUTF8StringEncoding];
    self.headersLength = [self.headers length];
    self.body          = [NSInputStream inputStreamWithFileAtPath:path];
    self.bodyLength    = [[[[NSFileManager defaultManager] attributesOfItemAtPath:path error:NULL] objectForKey:NSFileSize] unsignedIntegerValue];
    [self updateLength];
    return self;
}
- (id)initWithName:(NSString *)name filename:(NSString *)filename boundary:(NSString *)boundary stream:(NSInputStream *)stream streamLength:(NSUInteger)streamLength
{
    self.headers       = [[NSString stringWithFormat:kHeaderPathFormat, boundary, name, filename, MIMETypeForExtension(filename.pathExtension)] dataUsingEncoding:NSUTF8StringEncoding];
    self.headersLength = [self.headers length];
    self.body          = stream;
    self.bodyLength    = streamLength;
    [self updateLength];
    return self;
}
- (id)initWithHeaders:(NSDictionary *)headers string:(NSString *)string boundary:(NSString *)boundary
{
    self = [super init];
    if (self) {
        
        _headers = [self makeHeadersDataFromHeadersDict:headers boundary:boundary];
        _headersLength = _headers.length;
        NSData *stringData = [string dataUsingEncoding:NSUTF8StringEncoding];
        _body = [NSInputStream inputStreamWithData:stringData];
        _bodyLength = stringData.length;
        [self updateLength];
    }
    return self;
}

- (id)initWithHeaders:(NSDictionary *)headers path:(NSString *)path boundary:(NSString *)boundary
{
    self = [super init];
    if (self) {
        
        _headers = [self makeHeadersDataFromHeadersDict:headers boundary:boundary];
        _headersLength = _headers.length;
        _body = [NSInputStream inputStreamWithFileAtPath:path];
        _bodyLength = [[[[NSFileManager defaultManager] attributesOfItemAtPath:path error:NULL] objectForKey:NSFileSize] unsignedIntegerValue];
        [self updateLength];
    }
    return self;
}

#if TARGET_OS_IOS
- (id)initWithName:(NSString *)name filename:(NSString *)filename boundary:(NSString *)boundary asset:(ALAsset *)asset
{
    ALAssetRepresentation *rep = [asset defaultRepresentation];
    if (!filename) {
        filename = rep.filename;
    }
    NSString* mimeType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass
    ((__bridge CFStringRef)[rep UTI], kUTTagClassMIMEType);

    self.headers       = [[NSString stringWithFormat:kHeaderPathFormat, boundary, name, filename, mimeType] dataUsingEncoding:NSUTF8StringEncoding];
    self.headersLength = [self.headers length];
    self.body          = [NSInputStream pos_inputStreamWithAssetURL:rep.url];
    self.bodyLength    =  (NSUInteger)rep.size;
    [self updateLength];
    return self;
}
#endif

- (NSData *)makeHeadersDataFromHeadersDict:(NSDictionary *)headers boundary:(NSString *)boundary
{
    NSMutableString *headersString = [[NSMutableString alloc] initWithFormat:@"--%@", boundary];
    [self appendNewLine:headersString];
    
    for (NSString *key in headers.allKeys) {
        
        [headersString appendString:[[NSString alloc] initWithFormat:@"%@: %@", key, headers[key]]];
        [self appendNewLine:headersString];
    }
    
    [self appendNewLine:headersString];
    
    NSData *result = [headersString dataUsingEncoding:NSUTF8StringEncoding];
    return result;
}

- (void)appendNewLine:(NSMutableString *)string {
    
    [string appendString:@"\r\n"];
}

- (NSUInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len
{
    NSUInteger sent = 0, read;
    
    if (self.delivered >= self.length)
    {
        return 0;
    }
    if (self.delivered < self.headersLength && sent < len)
    {
        read            = MIN(self.headersLength - self.delivered, len - sent);
        [self.headers getBytes:buffer + sent range:NSMakeRange(self.delivered, read)];
        sent           += read;
        self.delivered += sent;
    }
    while (self.delivered >= self.headersLength && self.delivered < (self.length - 2) && sent < len)
    {
        if ((read = [self.body read:buffer + sent maxLength:len - sent]) == 0)
        {
            break;
        }
        sent           += read;
        self.delivered += read;
    }
    if (self.delivered >= (self.length - 2) && sent < len)
    {
        if (self.delivered == (self.length - 2))
        {
            *(buffer + sent) = '\r';
            sent ++; self.delivered ++;
        }
        *(buffer + sent) = '\n';
        sent ++; self.delivered ++;
    }
    return sent;
}
@end



================================================
FILE: Classes/QiniuToken.h
================================================
//
//  QiniuToken.h
//  QiniuUploadDemo
//
//  Created by 胡 桓铭 on 14-5-17.
//  Copyright (c) 2014年 hu. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface QiniuToken : NSObject


/**
 *  secretKey of cloud storage.
 */
@property (copy, nonatomic) NSString *secretKey;


/**
 *  accessKey of cloud storage.
 */
@property (copy, nonatomic) NSString *accessKey;


/**
 *  scope is a name of cloud storage.
 */
@property (copy, nonatomic) NSString *scope;


/**
 *  scope is a name of cloud storage.
 */
@property NSInteger liveTime;


+ (QiniuToken *)sharedQiniuToken;
/**
 *  uploadToken.
 */
- (NSString *)uploadToken;

/**
 *  initialize instance with scope, secret key and access key, the defaut live time is 5 minutes.
 *  @param scope scope is a name of cloud storage.
 *  @param SecretKey secretKey of cloud storage.
 *  @param Accesskey accessKey of cloud storage.
 */
+ (id)registerWithScope:(NSString *)theScope SecretKey:(NSString*)theSecretKey Accesskey:(NSString*)theAccessKey;


/**
 *  initialize instance with scope, secret key and access key.
 *  @param scope scope is a name of cloud storage.
 *  @param SecretKey secretKey of cloud storage.
 *  @param Accesskey accessKey of cloud storage.
 *  @param theliveTime the time to live of token.
 */

+ (id)registerWithScope:(NSString *)theScope SecretKey:(NSString*)theSecretKey Accesskey:(NSString*)theAccessKey TimeToLive:(NSInteger)theliveTime;


@end


================================================
FILE: Classes/QiniuToken.m
================================================
//
//  QiniuToken.m
//  QiniuUploadDemo
//
//  Created by 胡 桓铭 on 14-5-17.
//  Copyright (c) 2014年 hu. All rights reserved.
//

#import "QiniuToken.h"
#import <CommonCrypto/CommonHMAC.h>
#import <CommonCrypto/CommonCryptor.h>
#import "QUMDefines.h"
#import "QUMBase64.h"

@implementation QiniuToken

@synthesize scope;
@synthesize accessKey;
@synthesize secretKey;
@synthesize liveTime;

const static NSInteger defaultLiveTime  =  300;
static QiniuToken *qiniuToken = nil;


+ (id)registerWithScope:(NSString *)theScope SecretKey:(NSString*)theSecretKey Accesskey:(NSString*)theAccessKey{
    return [self registerWithScope:theScope SecretKey:theSecretKey Accesskey:theAccessKey TimeToLive:defaultLiveTime];
}

+ (id)registerWithScope:(NSString *)theScope SecretKey:(NSString*)theSecretKey Accesskey:(NSString*)theAccessKey TimeToLive:(NSInteger)theliveTime
{
    
    static dispatch_once_t predicate;
    
    dispatch_once(&predicate, ^{
        qiniuToken = [[QiniuToken alloc] init];
        qiniuToken.scope = theScope;
        qiniuToken.secretKey = theSecretKey;
        qiniuToken.accessKey = theAccessKey;
        qiniuToken.liveTime = theliveTime;
    });
    
    return self;
}

+ (QiniuToken *)sharedQiniuToken;
{
    return qiniuToken;
}

- (NSString *)uploadToken
{
    NSMutableDictionary *authInfo = [[NSMutableDictionary alloc]init];
    [authInfo setObject:scope forKey:@"scope"];
    [authInfo setObject:[NSNumber numberWithLong:[[NSDate date] timeIntervalSince1970]+self.liveTime] forKey:@"deadline" ];
    //    [authInfo setObject:@"" forKey:@"callbackUrl"];
    //    [authInfo setObject:@"" forKey:@"callbackBodyType"];
    //    [authInfo setObject:@"" forKey:@"customer"];
    //    [authInfo setObject:@"" forKey:@"escape"];
    //    [authInfo setObject:@"" forKey:@"asyncOps"];
    //    [authInfo setObject:@"" forKey:@"returnBody"];
    [authInfo setObject:[NSNumber numberWithInt:1] forKey:@"detectMime"];
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:authInfo
                                                       options:NSJSONWritingPrettyPrinted
                                                         error:nil];
    NSString *authInfoEncoded = [self urlSafeBase64Encode:jsonData];
    NSString *authDigestEncoded = [self hmac_sha1:secretKey text:authInfoEncoded];
    return [NSString stringWithFormat:@"%@:%@:%@",accessKey,authDigestEncoded,authInfoEncoded];
}


- (NSString *)hmac_sha1:(NSString *)key text:(NSString *)text{
    
    const char *cKey  = [key cStringUsingEncoding:NSUTF8StringEncoding];
    const char *cData = [text cStringUsingEncoding:NSUTF8StringEncoding];
    
    char cHMAC[CC_SHA1_DIGEST_LENGTH];
    
    CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
    
    NSData *HMAC = [[NSData alloc] initWithBytes:cHMAC length:CC_SHA1_DIGEST_LENGTH];
    NSString *hash = [self urlSafeBase64Encode:HMAC];
    return hash;
}

- (NSString *)urlSafeBase64Encode:(NSData *)text
{
    NSString *base64 = [[NSString alloc] initWithData:[QUMBase64 encodeData:text]
                                             encoding:NSUTF8StringEncoding];
    base64 = [base64 stringByReplacingOccurrencesOfString:@"+" withString:@"-"];
    base64 = [base64 stringByReplacingOccurrencesOfString:@"/" withString:@"_"];
    return base64;
}

- (NSString *)encodedEntryURI:(NSString*)entry
{
    
    return [self urlSafeBase64Encode:[[NSString stringWithFormat:@"motor:%@", [self encryptMD5String:entry]] dataUsingEncoding:NSUTF8StringEncoding]];
}

-(NSString*)encryptMD5String:(NSString*)string{
    const char *cStr = [string UTF8String];
    unsigned char result[32];
    CC_MD5( cStr, (CC_LONG)strlen(cStr),result );
    NSMutableString *hash =[NSMutableString string];
    for (int i = 0; i < 16; i++)
        [hash appendFormat:@"%02X", result[i]];
    return [hash lowercaseString];
}


@end


================================================
FILE: Classes/QiniuUploader.h
================================================
//
//  QiniuUploader.h
//  QiniuUploadDemo
//
//  Created by 胡 桓铭 on 14-5-17.
//  Copyright (c) 2014年 hu. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "QiniuToken.h"
#import "QiniuFile.h"
#import "QiniuInputStream.h"

@interface QiniuUploader : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate>

@property (assign, atomic) NSInteger maxConcurrentNumber;
@property (assign, atomic, readonly) Boolean isRunning;
@property (retain, atomic) NSArray * _Nonnull files;


+ (id _Nullable)sharedUploader;

/**
 *  start upload files to qiniu cloud storage.
 *  @param AccessToken Qiniu AccessToken from your sever
 *  @return Boolean if files were nil, it will return NO.
 */
- (Boolean)startUpload:(NSString * _Nonnull)theAccessToken
               uploadOneFileSucceededHandler: (nullable void (^)(NSInteger index, NSDictionary * _Nonnull info)) successHandler
             uploadOneFileFailedHandler: (nullable void (^)(NSInteger index, NSError * _Nullable error)) failHandler
             uploadOneFileProgressHandler: (nullable void (^)(NSInteger index, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend)) progressHandler
               uploadAllFilesComplete: (nullable void (^)()) completHandler;

/**
 *  cancel uploading task at once.
 */
- (Boolean)cancelAllUploadTask;


@end


================================================
FILE: Classes/QiniuUploader.m
================================================
//
//  QiniuUploader.m
//  QiniuUploadDemo
//
//  Created by 胡 桓铭 on 14-5-17.
//  Copyright (c) 2014年 hu. All rights reserved.
//

#import "QiniuUploader.h"

#define kQiniuUploadURL @"https://upload.qbox.me"
#define kQiniuTaskKey @"qiniuTaskKey"

typedef void (^UploadOneFileSucceededHandler)(NSInteger index, NSDictionary * _Nonnull info);
typedef void (^UploadOneFileFailedHandler)(NSInteger index, NSError * _Nullable error);
typedef void (^UploadOneFileProgressHandler)(NSInteger index, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend);
typedef void (^UploadAllFilesCompleteHandler)(void);

@implementation QiniuUploader{
    NSString *accessToken;
    NSMutableArray *fileQueue;
    NSMutableArray *operations;
    NSMutableDictionary *taskRefs;
    NSMutableDictionary *responsesData;
    __weak QiniuUploader *weakSelf;
    NSURLSession *defaultSession;
    UploadOneFileSucceededHandler oneSucceededHandler;
    UploadOneFileFailedHandler oneFailedHandler;
    UploadOneFileProgressHandler oneProgressHandler;
    UploadAllFilesCompleteHandler allCompleteHandler;
}

- (id)init
{
    if (self = [super init]) {
        _files = [[NSMutableArray alloc] init];
        _isRunning = NO;
        _maxConcurrentNumber = 1;

        fileQueue = [[NSMutableArray alloc] init];
        operations = [[NSMutableArray alloc] init];
        taskRefs = [[NSMutableDictionary alloc] init];
        responsesData = [[NSMutableDictionary alloc] init];
        
        weakSelf = self;
        
        NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
        config.timeoutIntervalForRequest = 15.0f;
        
        defaultSession = [NSURLSession sessionWithConfiguration:config delegate: self delegateQueue:nil];
        config = nil;
    }
    #ifdef DEBUG
        [QiniuUploader checkVersion];
    #endif
    return self;
}

+(id)sharedUploader
{
    static QiniuUploader *uploader;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if(uploader == nil)
            uploader = [[QiniuUploader alloc] init];
    });
    return uploader;
}

- (Boolean)startUpload:(NSString * _Nonnull)theAccessToken
        uploadOneFileSucceededHandler: (nullable void (^)(NSInteger index, NSDictionary * _Nonnull info)) successHandler
           uploadOneFileFailedHandler: (nullable void (^)(NSInteger index, NSError * _Nullable error)) failHandler
         uploadOneFileProgressHandler: (nullable void (^)(NSInteger index, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend)) progressHandler
               uploadAllFilesComplete: (nullable void (^)()) completeHandler
{
    accessToken = theAccessToken;
    oneSucceededHandler = successHandler;
    oneFailedHandler = failHandler;
    oneProgressHandler = progressHandler;
    allCompleteHandler = completeHandler;
    return [self startUpload];
}

- (Boolean)startUpload
{
    if (self.files.count == 0) {
        return false;
    }
    _isRunning = YES;
    [self createFileQueue];
    [self uploadQueue];
    return true;
}

- (void)createFileQueue {
    [fileQueue removeAllObjects];
    
    [self.files enumerateObjectsUsingBlock:
     ^(NSString *string, NSUInteger index, BOOL *stop)
     {
         [fileQueue addObject:@(index)];
     }];
}

- (NSInteger)deFileQueue {
  @synchronized (fileQueue) {
    if (fileQueue.count == 0) {
        return 0;
    }
    NSNumber *fileIndex = [fileQueue firstObject];
    [fileQueue removeObjectAtIndex:0];
    return fileIndex.intValue;
  }
}

- (void)uploadQueue {
    
    
    NSInteger poolSize = fileQueue.count < self.maxConcurrentNumber ? fileQueue.count : self.maxConcurrentNumber;
    
    for (NSUInteger i = 0; i < poolSize; i++) {
        NSInteger fileIndex = [weakSelf deFileQueue];
        [self uploadFile:fileIndex];
    }
}

- (void)uploadFile:(NSInteger)fileIndex
{
    
    QiniuFile *file = weakSelf.files[fileIndex];
    
    QiniuInputStream *inputStream = [[QiniuInputStream alloc] init];
    if (file.key) {
        [inputStream addPartWithName:@"key" string:file.key];
    }
    
    [inputStream addPartWithName:@"token" string: accessToken ?: [[QiniuToken sharedQiniuToken] uploadToken]];
    
    if (file.path) {
        [inputStream addPartWithName:@"file" path: file.path];
    }
    
    if (file.rawData){
        [inputStream addPartWithName:@"file" data: file.rawData];
    }
    
#if TARGET_OS_IOS
    if (file.asset) {
        [inputStream addPartWithName:@"file" asset:file.asset];
    }
#endif
    
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:kQiniuUploadURL]];
    [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", [inputStream boundary]] forHTTPHeaderField:@"Content-Type"];
    [request setValue:[NSString stringWithFormat:@"%ld", (unsigned long)[inputStream length]] forHTTPHeaderField:@"Content-Length"];
    [request setHTTPBodyStream:inputStream];
    [request setHTTPMethod:@"POST"];
    NSURLSessionTask * uploadTask = [defaultSession dataTaskWithRequest:request];
    
    NSString *taskDescription = [NSString stringWithFormat:@"%ld", fileIndex];

    @synchronized (taskRefs) {
        taskRefs[taskDescription] = uploadTask;
    }
    
    [uploadTask setTaskDescription:taskDescription];
    [uploadTask resume];
}

- (void)uploadComplete
{
    NSInteger fileIndex = [weakSelf deFileQueue];
    if (fileIndex > 0) {
        [weakSelf uploadFile:fileIndex];
    } else {
        @synchronized (taskRefs) {
            if (taskRefs.count == 0 && allCompleteHandler) {

                [fileQueue removeAllObjects];
                [responsesData removeAllObjects];
                _isRunning = NO;
                dispatch_async(dispatch_get_main_queue(), ^{
                    allCompleteHandler();
                    
                    oneSucceededHandler = nil;
                    oneFailedHandler = nil;
                    oneProgressHandler = nil;
                    allCompleteHandler = nil;
                });
            }
        }
    }
}

- (Boolean)cancelAllUploadTask
{
    [self stopUpload];
    return YES;
}

- (void)stopUpload
{
    @synchronized (taskRefs) {
        [taskRefs.allValues enumerateObjectsUsingBlock: ^(NSURLSessionTask *task, NSUInteger index, BOOL *stop)
         {
             [task suspend];
             [task cancel];
        }];
        [taskRefs removeAllObjects];
        [fileQueue removeAllObjects];
        _isRunning = NO;
    }
}

#pragma NSURLSessionTaskDelegate

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
   didSendBodyData:(int64_t)bytesSent
    totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
    NSInteger taskIndex = task.taskDescription.integerValue;
    if (oneProgressHandler) {
        dispatch_async(dispatch_get_main_queue(), ^{
            oneProgressHandler(taskIndex, bytesSent, totalBytesSent, totalBytesExpectedToSend);
        });
    }
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveData:(NSData *)data
{
    if (data) {
        responsesData[dataTask.taskDescription] = data;
    }
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    NSInteger fileIndex =  task.taskDescription.integerValue;
    if(error) {
        if (oneFailedHandler) {
            dispatch_async(dispatch_get_main_queue(), ^{
                oneFailedHandler(fileIndex, error);
            });
        }
    } else {
        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
        NSData *data = responsesData[task.taskDescription];
        if (httpResponse.statusCode == 200 && data) {
            NSDictionary *response = [NSJSONSerialization JSONObjectWithData:data options:(NSJSONReadingAllowFragments) error:nil];
            if (oneSucceededHandler) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    oneSucceededHandler(fileIndex, response);
                });
            }
        } else {
            if (oneFailedHandler) {
                error = [NSError errorWithDomain:kQiniuUploadURL code:httpResponse.statusCode userInfo:@{ NSLocalizedDescriptionKey : NSLocalizedString([NSString stringWithUTF8String:[data bytes]], @"")}];
                dispatch_async(dispatch_get_main_queue(), ^{
                    oneFailedHandler(fileIndex, error);
                });
            }
        }
        
    }

    @synchronized (taskRefs) {
        [taskRefs removeObjectForKey: task.taskDescription];
    }
    [weakSelf uploadComplete];
}

#pragma version


+ (NSString *)versionName {
    return @"3.0.0";
}

+ (NSInteger)version {
    return 15;
}

#ifdef DEBUG
+ (void)checkVersion {
    NSURLRequest *request =[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://raw.githubusercontent.com/huhuanming/qiniu_upload/master/Classes/version.json"]];
    NSURLSessionDataTask *checktask = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
        if (error) {
            NSLog(@"QiniuUpload cannot check updates, error:%@", error);
        } else {
            NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:(NSJSONReadingAllowFragments) error:nil];
            NSNumber *version = dic[@"version"];
            if (version.intValue > [self version]) {
                NSLog(@"QiniuUpload was updated! the new version is %@, but current version is %@. https://github.com/huhuanming/qiniu_upload, desc: %@", dic[@"versionName"], [self versionName], dic[@"desc"]);
            }
        }
    }];
    [checktask resume];
}
#endif

@end


================================================
FILE: Classes/version.json
================================================
{
  "versionName":"3.0.0",
  "version":16,
  "desc":"https://github.com/huhuanming/qiniu_upload/blob/master/CHANGELOG.md"
}


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2014 huhuanming

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

================================================
FILE: QiniuUpload.podspec
================================================
Pod::Spec.new do |s|

  s.name         = "QiniuUpload"
  s.version      = "3.0.0"
  s.summary      = "支持批量上传的七牛上传sdk,音频、视频、图片都是支持滴"

  s.description  = <<-DESC
                   A longer description of QiniuUpload in Markdown format.

                   * Think: Why did you write this? What is the focus? What does it do?
                   * CocoaPods will be using this to generate tags, and improve search results.
                   * Try to keep it short, snappy and to the point.
                   * Finally, don't worry about the indent, CocoaPods strips it!
                   DESC

  s.homepage     = "https://github.com/huhuanming/qiniu_upload"

  s.license      = "MIT"
  
  s.authors = { "huhuanming" => "workboring@gmail.com"}

  s.ios.deployment_target = '7.0'

  s.osx.deployment_target = '10.9'

  s.source       = { :git => "https://github.com/huhuanming/qiniu_upload.git", :tag => "3.0.0" }

  s.source_files  = "Classes", "Classes/*.{h,m}"
  
  s.ios.source_files   = 'Classes/POSInputStreamLibrary/*.{h,m}'
  
  #s.osx.source_files   = ''

  s.exclude_files = "Classes/Exclude"

  s.frameworks = 'Foundation'
  s.ios.frameworks = 'UIKit'
  s.osx.framework  = 'AppKit'

  s.requires_arc = true
end


================================================
FILE: QiniuUploadDemo/.gitignore
================================================

# Created by https://www.gitignore.io/api/xcode

### Xcode ###
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore

## Build generated
build/
DerivedData/

## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/

## Other
*.moved-aside
*.xccheckout
*.xcscmblueprint

================================================
FILE: QiniuUploadDemo/QiniuUploadDemo/AppDelegate.h
================================================
//
//  AppDelegate.h
//  QiniuUploadDemo
//
//  Created by 胡 桓铭 on 16/9/3.
//  Copyright © 2016年 胡 桓铭. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;


@end



================================================
FILE: QiniuUploadDemo/QiniuUploadDemo/AppDelegate.m
================================================
//
//  AppDelegate.m
//  QiniuUploadDemo
//
//  Created by 胡 桓铭 on 16/9/3.
//  Copyright © 2016年 胡 桓铭. All rights reserved.
//

#import "AppDelegate.h"
#import "ViewController.h"

@interface AppDelegate ()

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.rootViewController = [[ViewController alloc] init];
    [self.window makeKeyAndVisible];
    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application {
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}

- (void)applicationWillTerminate:(UIApplication *)application {
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

@end


================================================
FILE: QiniuUploadDemo/QiniuUploadDemo/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
  "images" : [
    {
      "idiom" : "iphone",
      "size" : "20x20",
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "size" : "20x20",
      "scale" : "3x"
    },
    {
      "idiom" : "iphone",
      "size" : "29x29",
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "size" : "29x29",
      "scale" : "3x"
    },
    {
      "idiom" : "iphone",
      "size" : "40x40",
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "size" : "40x40",
      "scale" : "3x"
    },
    {
      "idiom" : "iphone",
      "size" : "60x60",
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "size" : "60x60",
      "scale" : "3x"
    },
    {
      "idiom" : "ipad",
      "size" : "20x20",
      "scale" : "1x"
    },
    {
      "idiom" : "ipad",
      "size" : "20x20",
      "scale" : "2x"
    },
    {
      "idiom" : "ipad",
      "size" : "29x29",
      "scale" : "1x"
    },
    {
      "idiom" : "ipad",
      "size" : "29x29",
      "scale" : "2x"
    },
    {
      "idiom" : "ipad",
      "size" : "40x40",
      "scale" : "1x"
    },
    {
      "idiom" : "ipad",
      "size" : "40x40",
      "scale" : "2x"
    },
    {
      "idiom" : "ipad",
      "size" : "76x76",
      "scale" : "1x"
    },
    {
      "idiom" : "ipad",
      "size" : "76x76",
      "scale" : "2x"
    },
    {
      "idiom" : "ipad",
      "size" : "83.5x83.5",
      "scale" : "2x"
    }
  ],
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}

================================================
FILE: QiniuUploadDemo/QiniuUploadDemo/Base.lproj/LaunchScreen.storyboard
================================================
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="8150" systemVersion="15A204g" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
    <dependencies>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8122"/>
    </dependencies>
    <scenes>
        <!--View Controller-->
        <scene sceneID="EHf-IW-A2E">
            <objects>
                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
                    <layoutGuides>
                        <viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
                        <viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
                    </layoutGuides>
                    <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
                        <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <animations/>
                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
                    </view>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="53" y="375"/>
        </scene>
    </scenes>
</document>


================================================
FILE: QiniuUploadDemo/QiniuUploadDemo/Base.lproj/Main.storyboard
================================================
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6211" systemVersion="14A298i" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
    <dependencies>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6204"/>
    </dependencies>
    <scenes>
        <!--View Controller-->
        <scene sceneID="tne-QT-ifu">
            <objects>
                <viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="" sceneMemberID="viewController">
                    <layoutGuides>
                        <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
                        <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
                    </layoutGuides>
                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
                        <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
                    </view>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
            </objects>
        </scene>
    </scenes>
</document>


================================================
FILE: QiniuUploadDemo/QiniuUploadDemo/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>
	<key>CFBundleSignature</key>
	<string>????</string>
	<key>CFBundleVersion</key>
	<string>1</string>
	<key>LSRequiresIPhoneOS</key>
	<true/>
	<key>NSAppTransportSecurity</key>
	<dict>
		<key>NSAllowsArbitraryLoads</key>
		<true/>
	</dict>
	<key>NSPhotoLibraryUsageDescription</key>
	<string></string>
	<key>UILaunchStoryboardName</key>
	<string>LaunchScreen</string>
	<key>UIRequiredDeviceCapabilities</key>
	<array>
		<string>armv7</string>
	</array>
	<key>UISupportedInterfaceOrientations</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
	<key>UISupportedInterfaceOrientations~ipad</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationPortraitUpsideDown</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
</dict>
</plist>


================================================
FILE: QiniuUploadDemo/QiniuUploadDemo/ViewController.h
================================================
//
//  ViewController.h
//  QiniuUploadDemo
//
//  Created by 胡 桓铭 on 16/9/3.
//  Copyright © 2016年 胡 桓铭. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <AssetsLibrary/AssetsLibrary.h>

@interface ViewController : UIViewController


@end



================================================
FILE: QiniuUploadDemo/QiniuUploadDemo/ViewController.m
================================================
//
//  ViewController.m
//  QiniuUploadDemo
//
//  Created by 胡 桓铭 on 16/9/3.
//  Copyright © 2016年 胡 桓铭. All rights reserved.
//

#import "ViewController.h"
#import "QiniuUploader.h"

@interface ViewController ()<UINavigationControllerDelegate,UIImagePickerControllerDelegate,
UIAlertViewDelegate>{
    UIImageView *imageView;
    QiniuUploader *uploader;
}
@end

@implementation ViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        
    }
    return self;
}

- (void)viewDidLoad
{
    
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    imageView = [[UIImageView alloc] initWithFrame:CGRectMake(40, 40, 260, 200)];
    [imageView setImage:[UIImage imageNamed:@"test.jpg"]];
    [self.view addSubview:imageView];
    
    
    UIButton *imageSelectedButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [imageSelectedButton setFrame:CGRectMake(60,260, 100, 50)];
    [imageSelectedButton setTitle:@"select image" forState:UIControlStateNormal];
    [self.view addSubview:imageSelectedButton];
    [imageSelectedButton addTarget:self action:@selector(imageSelectedClick:) forControlEvents:UIControlEventTouchUpInside];
    
    UIButton *imageUploaderButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [imageUploaderButton setFrame:CGRectMake(20,300, 100, 50)];
    [imageUploaderButton setTitle:@"upload images" forState:UIControlStateNormal];
    [self.view addSubview:imageUploaderButton];
    [imageUploaderButton addTarget:self action:@selector(imageUploadClick:) forControlEvents:UIControlEventTouchUpInside];
    
    
    UIButton *audioUploaderButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [audioUploaderButton setFrame:CGRectMake(140,300, 100, 50)];
    [audioUploaderButton setTitle:@"upload audios" forState:UIControlStateNormal];
    [self.view addSubview:audioUploaderButton];
    [audioUploaderButton addTarget:self action:@selector(audioUploadClick:) forControlEvents:UIControlEventTouchUpInside];
    
    
    UIButton *stopButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [stopButton setFrame:CGRectMake(240,300, 100, 50)];
    [stopButton setTitle:@"stop All!" forState:UIControlStateNormal];
    [self.view addSubview:stopButton];
    [stopButton addTarget:self action:@selector(stopClick:) forControlEvents:UIControlEventTouchUpInside];
    
    //register qiniu
    [QiniuToken registerWithScope:@"temp" SecretKey:@"SK" Accesskey:@"AK"];
    NSLog(@"%@",[[QiniuToken sharedQiniuToken] uploadToken]);
    [[QiniuUploader sharedUploader] setMaxConcurrentNumber:3];
}

// click events

- (void)imageSelectedClick:(id)sender
{
    UIImagePickerController *pickerController = [[UIImagePickerController alloc] init];
    pickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
    pickerController.delegate = self;
    pickerController.allowsEditing = YES; //是否可编辑
    [self presentViewController:pickerController animated:YES completion:nil];
}

- (void)imageUploadClick:(id)sender
{
    [self uploadImageFiles];
}

- (void)audioUploadClick:(id)sender
{
    [self uploadAudio];
}

- (void)stopClick:(id)sender
{
    [uploader cancelAllUploadTask];
    NSLog(@"All uploading task stop now!!!");
}

// Upload

- (void)uploadImageFiles
{
    if (uploader.isRunning) {
        NSLog(@"不要启动太多上传任务,会很累的诶");
        return;
    }

    //add file
    QiniuFile *file = [[QiniuFile alloc] initWithFileData:UIImageJPEGRepresentation(imageView.image, 1.0f)];
    
    //startUpload
    uploader = [QiniuUploader sharedUploader];
    [uploader setFiles:@[file, file, file, file, file, file]];
    
    [uploader startUpload:[QiniuToken sharedQiniuToken].uploadToken uploadOneFileSucceededHandler:^(NSInteger index, NSDictionary * _Nonnull info) {
        NSLog(@"index: %ld info: %@",(long)index, info);
    } uploadOneFileFailedHandler:^(NSInteger index, NSError * _Nullable error) {
        NSLog(@"error: %@", error);
    } uploadOneFileProgressHandler:^(NSInteger index, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) {
        NSLog(@"index:%ld percent:%f",(long)index, totalBytesSent * 1.0 / totalBytesExpectedToSend);
    } uploadAllFilesComplete:^{
        NSLog(@"complete");
    }];
}

- (void)uploadAudio
{
    if (uploader.isRunning) {
        NSLog(@"不要启动太多上传任务,会很累的诶");
        return;
    }

    //add file
    NSString *path = [NSString stringWithFormat:@"%@/%@",[NSBundle mainBundle].resourcePath,@"ふつうのdisco.mp3"];
    QiniuFile *file = [[QiniuFile alloc] initWithPath:path];
    //startUpload
    uploader = [QiniuUploader sharedUploader];
    uploader.files = @[file, file, file, file];
    
    [uploader startUpload:[QiniuToken sharedQiniuToken].uploadToken uploadOneFileSucceededHandler:^(NSInteger index, NSDictionary * _Nonnull info) {
        NSLog(@"index: %ld info: %@",(long)index, info);
    } uploadOneFileFailedHandler:^(NSInteger index, NSError * _Nullable error) {
        NSLog(@"error: %@", error);
    } uploadOneFileProgressHandler:^(NSInteger index, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) {
        NSLog(@"index:%ld percent:%f",(long)index, totalBytesSent * 1.0 / totalBytesExpectedToSend);
    } uploadAllFilesComplete:^{
        NSLog(@"complete");
    }];
}

// UIImagePickerControllerdelegate

- (void)imagePickerController:(UIImagePickerController *)pickerController didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    [pickerController dismissViewControllerAnimated:YES completion:nil];
    NSLog(@"%@",info);
    
    [imageView setImage:(UIImage*)info[@"UIImagePickerControllerOriginalImage"]];
    NSURL *url = info[@"UIImagePickerControllerReferenceURL"];
    
    
    [[[ALAssetsLibrary alloc] init] assetForURL:url resultBlock:^(ALAsset *asset) {
        if (uploader.isRunning) {
            NSLog(@"不要启动太多上传任务,会很累的诶");
            return;
        }
        
        uploader = [QiniuUploader sharedUploader];
        
        QiniuFile *file = [[QiniuFile alloc] initWithAsset:asset];
        uploader.files = @[file];
        
        [uploader startUpload:[QiniuToken sharedQiniuToken].uploadToken uploadOneFileSucceededHandler:^(NSInteger index, NSDictionary * _Nonnull info) {
            NSLog(@"index: %ld info: %@",(long)index, info);
        } uploadOneFileFailedHandler:^(NSInteger index, NSError * _Nullable error) {
            NSLog(@"error: %@", error);
        } uploadOneFileProgressHandler:^(NSInteger index, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) {
             NSLog(@"index:%ld percent:%f",(long)index, totalBytesSent * 1.0 / totalBytesExpectedToSend);
        } uploadAllFilesComplete:^{
            NSLog(@"complete");
        }];
    } failureBlock: nil];
}

@end


================================================
FILE: QiniuUploadDemo/QiniuUploadDemo/main.m
================================================
//
//  main.m
//  QiniuUploadDemo
//
//  Created by 胡 桓铭 on 16/9/3.
//  Copyright © 2016年 胡 桓铭. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "AppDelegate.h"

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}


================================================
FILE: QiniuUploadDemo/QiniuUploadDemo.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
	archiveVersion = 1;
	classes = {
	};
	objectVersion = 46;
	objects = {

/* Begin PBXBuildFile section */
		8535AB9A1D7AC99A0096FFE5 /* QiniuInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = 8535AB991D7AC99A0096FFE5 /* QiniuInputStream.m */; };
		8535ABA01D7ACAE70096FFE5 /* QiniuMultipartElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 8535AB9F1D7ACAE70096FFE5 /* QiniuMultipartElement.m */; };
		8535ABB71D7B13350096FFE5 /* ALAssetsLibrary+POS.m in Sources */ = {isa = PBXBuildFile; fileRef = 8535ABA31D7B13350096FFE5 /* ALAssetsLibrary+POS.m */; };
		8535ABB81D7B13350096FFE5 /* NSInputStream+POS.m in Sources */ = {isa = PBXBuildFile; fileRef = 8535ABA51D7B13350096FFE5 /* NSInputStream+POS.m */; };
		8535ABB91D7B13350096FFE5 /* POSAdjustedAssetReaderIOS7.m in Sources */ = {isa = PBXBuildFile; fileRef = 8535ABA71D7B13350096FFE5 /* POSAdjustedAssetReaderIOS7.m */; };
		8535ABBA1D7B13350096FFE5 /* POSAdjustedAssetReaderIOS8.m in Sources */ = {isa = PBXBuildFile; fileRef = 8535ABA91D7B13350096FFE5 /* POSAdjustedAssetReaderIOS8.m */; };
		8535ABBB1D7B13350096FFE5 /* POSBlobInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = 8535ABAD1D7B13350096FFE5 /* POSBlobInputStream.m */; };
		8535ABBC1D7B13350096FFE5 /* POSBlobInputStreamAssetDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 8535ABAF1D7B13350096FFE5 /* POSBlobInputStreamAssetDataSource.m */; };
		8535ABBD1D7B13350096FFE5 /* POSFastAssetReader.m in Sources */ = {isa = PBXBuildFile; fileRef = 8535ABB21D7B13350096FFE5 /* POSFastAssetReader.m */; };
		8535ABBE1D7B13350096FFE5 /* POSLocking.m in Sources */ = {isa = PBXBuildFile; fileRef = 8535ABB61D7B13350096FFE5 /* POSLocking.m */; };
		8581022A1D7ABF1900B8FCB2 /* QiniuFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 858102211D7ABF1900B8FCB2 /* QiniuFile.m */; };
		8581022B1D7ABF1900B8FCB2 /* QiniuFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 858102211D7ABF1900B8FCB2 /* QiniuFile.m */; };
		8581022C1D7ABF1900B8FCB2 /* QiniuFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 858102211D7ABF1900B8FCB2 /* QiniuFile.m */; };
		8581022D1D7ABF1900B8FCB2 /* QiniuToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 858102231D7ABF1900B8FCB2 /* QiniuToken.m */; };
		8581022E1D7ABF1900B8FCB2 /* QiniuToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 858102231D7ABF1900B8FCB2 /* QiniuToken.m */; };
		8581022F1D7ABF1900B8FCB2 /* QiniuToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 858102231D7ABF1900B8FCB2 /* QiniuToken.m */; };
		858102301D7ABF1900B8FCB2 /* QiniuUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = 858102251D7ABF1900B8FCB2 /* QiniuUploader.m */; };
		858102311D7ABF1900B8FCB2 /* QiniuUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = 858102251D7ABF1900B8FCB2 /* QiniuUploader.m */; };
		858102321D7ABF1900B8FCB2 /* QiniuUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = 858102251D7ABF1900B8FCB2 /* QiniuUploader.m */; };
		858102331D7ABF1900B8FCB2 /* QUMBase64.m in Sources */ = {isa = PBXBuildFile; fileRef = 858102271D7ABF1900B8FCB2 /* QUMBase64.m */; };
		858102341D7ABF1900B8FCB2 /* QUMBase64.m in Sources */ = {isa = PBXBuildFile; fileRef = 858102271D7ABF1900B8FCB2 /* QUMBase64.m */; };
		858102351D7ABF1900B8FCB2 /* QUMBase64.m in Sources */ = {isa = PBXBuildFile; fileRef = 858102271D7ABF1900B8FCB2 /* QUMBase64.m */; };
		858102361D7ABF1900B8FCB2 /* version.json in Resources */ = {isa = PBXBuildFile; fileRef = 858102291D7ABF1900B8FCB2 /* version.json */; };
		858102371D7ABF1900B8FCB2 /* version.json in Resources */ = {isa = PBXBuildFile; fileRef = 858102291D7ABF1900B8FCB2 /* version.json */; };
		858102381D7ABF1900B8FCB2 /* version.json in Resources */ = {isa = PBXBuildFile; fileRef = 858102291D7ABF1900B8FCB2 /* version.json */; };
		85DD383D1D7ABA3F006A77C2 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 85DD383C1D7ABA3F006A77C2 /* main.m */; };
		85DD38401D7ABA3F006A77C2 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 85DD383F1D7ABA3F006A77C2 /* AppDelegate.m */; };
		85DD38431D7ABA3F006A77C2 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 85DD38421D7ABA3F006A77C2 /* ViewController.m */; };
		85DD38481D7ABA3F006A77C2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 85DD38471D7ABA3F006A77C2 /* Assets.xcassets */; };
		85DD384B1D7ABA3F006A77C2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 85DD38491D7ABA3F006A77C2 /* LaunchScreen.storyboard */; };
		85DD38561D7ABA3F006A77C2 /* QiniuUploadDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 85DD38551D7ABA3F006A77C2 /* QiniuUploadDemoTests.m */; };
		85DD38611D7ABA3F006A77C2 /* QiniuUploadDemoUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 85DD38601D7ABA3F006A77C2 /* QiniuUploadDemoUITests.m */; };
		85DD38901D7ABC98006A77C2 /* test.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 85DD388E1D7ABC98006A77C2 /* test.jpg */; };
		85DD38911D7ABC98006A77C2 /* test.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 85DD388E1D7ABC98006A77C2 /* test.jpg */; };
		85DD38921D7ABC98006A77C2 /* test.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 85DD388E1D7ABC98006A77C2 /* test.jpg */; };
		85DD38931D7ABC98006A77C2 /* ふつうのdisco.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 85DD388F1D7ABC98006A77C2 /* ふつうのdisco.mp3 */; };
		85DD38941D7ABC98006A77C2 /* ふつうのdisco.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 85DD388F1D7ABC98006A77C2 /* ふつうのdisco.mp3 */; };
		85DD38951D7ABC98006A77C2 /* ふつうのdisco.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 85DD388F1D7ABC98006A77C2 /* ふつうのdisco.mp3 */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
		85DD38521D7ABA3F006A77C2 /* PBXContainerItemProxy */ = {
			isa = PBXContainerItemProxy;
			containerPortal = 85DD38301D7ABA3F006A77C2 /* Project object */;
			proxyType = 1;
			remoteGlobalIDString = 85DD38371D7ABA3F006A77C2;
			remoteInfo = QiniuUploadDemo;
		};
		85DD385D1D7ABA3F006A77C2 /* PBXContainerItemProxy */ = {
			isa = PBXContainerItemProxy;
			containerPortal = 85DD38301D7ABA3F006A77C2 /* Project object */;
			proxyType = 1;
			remoteGlobalIDString = 85DD38371D7ABA3F006A77C2;
			remoteInfo = QiniuUploadDemo;
		};
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
		8535AB981D7AC99A0096FFE5 /* QiniuInputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QiniuInputStream.h; sourceTree = "<group>"; };
		8535AB991D7AC99A0096FFE5 /* QiniuInputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QiniuInputStream.m; sourceTree = "<group>"; };
		8535AB9E1D7ACAE70096FFE5 /* QiniuMultipartElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QiniuMultipartElement.h; sourceTree = "<group>"; };
		8535AB9F1D7ACAE70096FFE5 /* QiniuMultipartElement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QiniuMultipartElement.m; sourceTree = "<group>"; };
		8535ABA21D7B13350096FFE5 /* ALAssetsLibrary+POS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ALAssetsLibrary+POS.h"; sourceTree = "<group>"; };
		8535ABA31D7B13350096FFE5 /* ALAssetsLibrary+POS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "ALAssetsLibrary+POS.m"; sourceTree = "<group>"; };
		8535ABA41D7B13350096FFE5 /* NSInputStream+POS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSInputStream+POS.h"; sourceTree = "<group>"; };
		8535ABA51D7B13350096FFE5 /* NSInputStream+POS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSInputStream+POS.m"; sourceTree = "<group>"; };
		8535ABA61D7B13350096FFE5 /* POSAdjustedAssetReaderIOS7.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = POSAdjustedAssetReaderIOS7.h; sourceTree = "<group>"; };
		8535ABA71D7B13350096FFE5 /* POSAdjustedAssetReaderIOS7.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = POSAdjustedAssetReaderIOS7.m; sourceTree = "<group>"; };
		8535ABA81D7B13350096FFE5 /* POSAdjustedAssetReaderIOS8.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = POSAdjustedAssetReaderIOS8.h; sourceTree = "<group>"; };
		8535ABA91D7B13350096FFE5 /* POSAdjustedAssetReaderIOS8.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = POSAdjustedAssetReaderIOS8.m; sourceTree = "<group>"; };
		8535ABAA1D7B13350096FFE5 /* POSAssetReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = POSAssetReader.h; sourceTree = "<group>"; };
		8535ABAB1D7B13350096FFE5 /* POSBlobInputStream-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "POSBlobInputStream-Prefix.pch"; sourceTree = "<group>"; };
		8535ABAC1D7B13350096FFE5 /* POSBlobInputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = POSBlobInputStream.h; sourceTree = "<group>"; };
		8535ABAD1D7B13350096FFE5 /* POSBlobInputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = POSBlobInputStream.m; sourceTree = "<group>"; };
		8535ABAE1D7B13350096FFE5 /* POSBlobInputStreamAssetDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = POSBlobInputStreamAssetDataSource.h; sourceTree = "<group>"; };
		8535ABAF1D7B13350096FFE5 /* POSBlobInputStreamAssetDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = POSBlobInputStreamAssetDataSource.m; sourceTree = "<group>"; };
		8535ABB01D7B13350096FFE5 /* POSBlobInputStreamDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = POSBlobInputStreamDataSource.h; sourceTree = "<group>"; };
		8535ABB11D7B13350096FFE5 /* POSFastAssetReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = POSFastAssetReader.h; sourceTree = "<group>"; };
		8535ABB21D7B13350096FFE5 /* POSFastAssetReader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = POSFastAssetReader.m; sourceTree = "<group>"; };
		8535ABB31D7B13350096FFE5 /* POSInputStreamLibrary-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "POSInputStreamLibrary-Prefix.pch"; sourceTree = "<group>"; };
		8535ABB41D7B13350096FFE5 /* POSInputStreamLibrary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = POSInputStreamLibrary.h; sourceTree = "<group>"; };
		8535ABB51D7B13350096FFE5 /* POSLocking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = POSLocking.h; sourceTree = "<group>"; };
		8535ABB61D7B13350096FFE5 /* POSLocking.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = POSLocking.m; sourceTree = "<group>"; };
		858102201D7ABF1900B8FCB2 /* QiniuFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QiniuFile.h; sourceTree = "<group>"; };
		858102211D7ABF1900B8FCB2 /* QiniuFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QiniuFile.m; sourceTree = "<group>"; };
		858102221D7ABF1900B8FCB2 /* QiniuToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QiniuToken.h; sourceTree = "<group>"; };
		858102231D7ABF1900B8FCB2 /* QiniuToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QiniuToken.m; sourceTree = "<group>"; };
		858102241D7ABF1900B8FCB2 /* QiniuUploader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QiniuUploader.h; sourceTree = "<group>"; };
		858102251D7ABF1900B8FCB2 /* QiniuUploader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QiniuUploader.m; sourceTree = "<group>"; };
		858102261D7ABF1900B8FCB2 /* QUMBase64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QUMBase64.h; sourceTree = "<group>"; };
		858102271D7ABF1900B8FCB2 /* QUMBase64.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QUMBase64.m; sourceTree = "<group>"; };
		858102281D7ABF1900B8FCB2 /* QUMDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QUMDefines.h; sourceTree = "<group>"; };
		858102291D7ABF1900B8FCB2 /* version.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = version.json; sourceTree = "<group>"; };
		85DD38381D7ABA3F006A77C2 /* QiniuUploadDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = QiniuUploadDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
		85DD383C1D7ABA3F006A77C2 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
		85DD383E1D7ABA3F006A77C2 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
		85DD383F1D7ABA3F006A77C2 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
		85DD38411D7ABA3F006A77C2 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
		85DD38421D7ABA3F006A77C2 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
		85DD38471D7ABA3F006A77C2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
		85DD384A1D7ABA3F006A77C2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
		85DD384C1D7ABA3F006A77C2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
		85DD38511D7ABA3F006A77C2 /* QiniuUploadDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = QiniuUploadDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
		85DD38551D7ABA3F006A77C2 /* QiniuUploadDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QiniuUploadDemoTests.m; sourceTree = "<group>"; };
		85DD38571D7ABA3F006A77C2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
		85DD385C1D7ABA3F006A77C2 /* QiniuUploadDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = QiniuUploadDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
		85DD38601D7ABA3F006A77C2 /* QiniuUploadDemoUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QiniuUploadDemoUITests.m; sourceTree = "<group>"; };
		85DD38621D7ABA3F006A77C2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
		85DD388E1D7ABC98006A77C2 /* test.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = test.jpg; sourceTree = "<group>"; };
		85DD388F1D7ABC98006A77C2 /* ふつうのdisco.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = "ふつうのdisco.mp3"; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
		85DD38351D7ABA3F006A77C2 /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		85DD384E1D7ABA3F006A77C2 /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		85DD38591D7ABA3F006A77C2 /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
		8535ABA11D7B13350096FFE5 /* POSInputStreamLibrary */ = {
			isa = PBXGroup;
			children = (
				8535ABA21D7B13350096FFE5 /* ALAssetsLibrary+POS.h */,
				8535ABA31D7B13350096FFE5 /* ALAssetsLibrary+POS.m */,
				8535ABA41D7B13350096FFE5 /* NSInputStream+POS.h */,
				8535ABA51D7B13350096FFE5 /* NSInputStream+POS.m */,
				8535ABA61D7B13350096FFE5 /* POSAdjustedAssetReaderIOS7.h */,
				8535ABA71D7B13350096FFE5 /* POSAdjustedAssetReaderIOS7.m */,
				8535ABA81D7B13350096FFE5 /* POSAdjustedAssetReaderIOS8.h */,
				8535ABA91D7B13350096FFE5 /* POSAdjustedAssetReaderIOS8.m */,
				8535ABAA1D7B13350096FFE5 /* POSAssetReader.h */,
				8535ABAB1D7B13350096FFE5 /* POSBlobInputStream-Prefix.pch */,
				8535ABAC1D7B13350096FFE5 /* POSBlobInputStream.h */,
				8535ABAD1D7B13350096FFE5 /* POSBlobInputStream.m */,
				8535ABAE1D7B13350096FFE5 /* POSBlobInputStreamAssetDataSource.h */,
				8535ABAF1D7B13350096FFE5 /* POSBlobInputStreamAssetDataSource.m */,
				8535ABB01D7B13350096FFE5 /* POSBlobInputStreamDataSource.h */,
				8535ABB11D7B13350096FFE5 /* POSFastAssetReader.h */,
				8535ABB21D7B13350096FFE5 /* POSFastAssetReader.m */,
				8535ABB31D7B13350096FFE5 /* POSInputStreamLibrary-Prefix.pch */,
				8535ABB41D7B13350096FFE5 /* POSInputStreamLibrary.h */,
				8535ABB51D7B13350096FFE5 /* POSLocking.h */,
				8535ABB61D7B13350096FFE5 /* POSLocking.m */,
			);
			path = POSInputStreamLibrary;
			sourceTree = "<group>";
		};
		8581021F1D7ABF1900B8FCB2 /* Classes */ = {
			isa = PBXGroup;
			children = (
				8535ABA11D7B13350096FFE5 /* POSInputStreamLibrary */,
				858102281D7ABF1900B8FCB2 /* QUMDefines.h */,
				858102201D7ABF1900B8FCB2 /* QiniuFile.h */,
				858102211D7ABF1900B8FCB2 /* QiniuFile.m */,
				858102221D7ABF1900B8FCB2 /* QiniuToken.h */,
				858102231D7ABF1900B8FCB2 /* QiniuToken.m */,
				8535AB9E1D7ACAE70096FFE5 /* QiniuMultipartElement.h */,
				8535AB9F1D7ACAE70096FFE5 /* QiniuMultipartElement.m */,
				8535AB981D7AC99A0096FFE5 /* QiniuInputStream.h */,
				8535AB991D7AC99A0096FFE5 /* QiniuInputStream.m */,
				858102241D7ABF1900B8FCB2 /* QiniuUploader.h */,
				858102251D7ABF1900B8FCB2 /* QiniuUploader.m */,
				858102261D7ABF1900B8FCB2 /* QUMBase64.h */,
				858102271D7ABF1900B8FCB2 /* QUMBase64.m */,
				858102291D7ABF1900B8FCB2 /* version.json */,
			);
			name = Classes;
			path = ../../Classes;
			sourceTree = "<group>";
		};
		85DD382F1D7ABA3F006A77C2 = {
			isa = PBXGroup;
			children = (
				85DD383A1D7ABA3F006A77C2 /* QiniuUploadDemo */,
				85DD38541D7ABA3F006A77C2 /* QiniuUploadDemoTests */,
				85DD385F1D7ABA3F006A77C2 /* QiniuUploadDemoUITests */,
				85DD38391D7ABA3F006A77C2 /* Products */,
			);
			sourceTree = "<group>";
		};
		85DD38391D7ABA3F006A77C2 /* Products */ = {
			isa = PBXGroup;
			children = (
				85DD38381D7ABA3F006A77C2 /* QiniuUploadDemo.app */,
				85DD38511D7ABA3F006A77C2 /* QiniuUploadDemoTests.xctest */,
				85DD385C1D7ABA3F006A77C2 /* QiniuUploadDemoUITests.xctest */,
			);
			name = Products;
			sourceTree = "<group>";
		};
		85DD383A1D7ABA3F006A77C2 /* QiniuUploadDemo */ = {
			isa = PBXGroup;
			children = (
				8581021F1D7ABF1900B8FCB2 /* Classes */,
				85DD388E1D7ABC98006A77C2 /* test.jpg */,
				85DD388F1D7ABC98006A77C2 /* ふつうのdisco.mp3 */,
				85DD383E1D7ABA3F006A77C2 /* AppDelegate.h */,
				85DD383F1D7ABA3F006A77C2 /* AppDelegate.m */,
				85DD38411D7ABA3F006A77C2 /* ViewController.h */,
				85DD38421D7ABA3F006A77C2 /* ViewController.m */,
				85DD38471D7ABA3F006A77C2 /* Assets.xcassets */,
				85DD38491D7ABA3F006A77C2 /* LaunchScreen.storyboard */,
				85DD384C1D7ABA3F006A77C2 /* Info.plist */,
				85DD383B1D7ABA3F006A77C2 /* Supporting Files */,
			);
			path = QiniuUploadDemo;
			sourceTree = "<group>";
		};
		85DD383B1D7ABA3F006A77C2 /* Supporting Files */ = {
			isa = PBXGroup;
			children = (
				85DD383C1D7ABA3F006A77C2 /* main.m */,
			);
			name = "Supporting Files";
			sourceTree = "<group>";
		};
		85DD38541D7ABA3F006A77C2 /* QiniuUploadDemoTests */ = {
			isa = PBXGroup;
			children = (
				85DD38551D7ABA3F006A77C2 /* QiniuUploadDemoTests.m */,
				85DD38571D7ABA3F006A77C2 /* Info.plist */,
			);
			path = QiniuUploadDemoTests;
			sourceTree = "<group>";
		};
		85DD385F1D7ABA3F006A77C2 /* QiniuUploadDemoUITests */ = {
			isa = PBXGroup;
			children = (
				85DD38601D7ABA3F006A77C2 /* QiniuUploadDemoUITests.m */,
				85DD38621D7ABA3F006A77C2 /* Info.plist */,
			);
			path = QiniuUploadDemoUITests;
			sourceTree = "<group>";
		};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
		85DD38371D7ABA3F006A77C2 /* QiniuUploadDemo */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = 85DD38651D7ABA3F006A77C2 /* Build configuration list for PBXNativeTarget "QiniuUploadDemo" */;
			buildPhases = (
				85DD38341D7ABA3F006A77C2 /* Sources */,
				85DD38351D7ABA3F006A77C2 /* Frameworks */,
				85DD38361D7ABA3F006A77C2 /* Resources */,
			);
			buildRules = (
			);
			dependencies = (
			);
			name = QiniuUploadDemo;
			productName = QiniuUploadDemo;
			productReference = 85DD38381D7ABA3F006A77C2 /* QiniuUploadDemo.app */;
			productType = "com.apple.product-type.application";
		};
		85DD38501D7ABA3F006A77C2 /* QiniuUploadDemoTests */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = 85DD38681D7ABA3F006A77C2 /* Build configuration list for PBXNativeTarget "QiniuUploadDemoTests" */;
			buildPhases = (
				85DD384D1D7ABA3F006A77C2 /* Sources */,
				85DD384E1D7ABA3F006A77C2 /* Frameworks */,
				85DD384F1D7ABA3F006A77C2 /* Resources */,
			);
			buildRules = (
			);
			dependencies = (
				85DD38531D7ABA3F006A77C2 /* PBXTargetDependency */,
			);
			name = QiniuUploadDemoTests;
			productName = QiniuUploadDemoTests;
			productReference = 85DD38511D7ABA3F006A77C2 /* QiniuUploadDemoTests.xctest */;
			productType = "com.apple.product-type.bundle.unit-test";
		};
		85DD385B1D7ABA3F006A77C2 /* QiniuUploadDemoUITests */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = 85DD386B1D7ABA3F006A77C2 /* Build configuration list for PBXNativeTarget "QiniuUploadDemoUITests" */;
			buildPhases = (
				85DD38581D7ABA3F006A77C2 /* Sources */,
				85DD38591D7ABA3F006A77C2 /* Frameworks */,
				85DD385A1D7ABA3F006A77C2 /* Resources */,
			);
			buildRules = (
			);
			dependencies = (
				85DD385E1D7ABA3F006A77C2 /* PBXTargetDependency */,
			);
			name = QiniuUploadDemoUITests;
			productName = QiniuUploadDemoUITests;
			productReference = 85DD385C1D7ABA3F006A77C2 /* QiniuUploadDemoUITests.xctest */;
			productType = "com.apple.product-type.bundle.ui-testing";
		};
/* End PBXNativeTarget section */

/* Begin PBXProject section */
		85DD38301D7ABA3F006A77C2 /* Project object */ = {
			isa = PBXProject;
			attributes = {
				LastUpgradeCheck = 0800;
				ORGANIZATIONNAME = "胡 桓铭";
				TargetAttributes = {
					85DD38371D7ABA3F006A77C2 = {
						CreatedOnToolsVersion = 7.3.1;
						DevelopmentTeam = 5M34M6CU7U;
					};
					85DD38501D7ABA3F006A77C2 = {
						CreatedOnToolsVersion = 7.3.1;
						DevelopmentTeam = 5M34M6CU7U;
						TestTargetID = 85DD38371D7ABA3F006A77C2;
					};
					85DD385B1D7ABA3F006A77C2 = {
						CreatedOnToolsVersion = 7.3.1;
						DevelopmentTeam = 5M34M6CU7U;
						TestTargetID = 85DD38371D7ABA3F006A77C2;
					};
				};
			};
			buildConfigurationList = 85DD38331D7ABA3F006A77C2 /* Build configuration list for PBXProject "QiniuUploadDemo" */;
			compatibilityVersion = "Xcode 3.2";
			developmentRegion = English;
			hasScannedForEncodings = 0;
			knownRegions = (
				en,
				Base,
			);
			mainGroup = 85DD382F1D7ABA3F006A77C2;
			productRefGroup = 85DD38391D7ABA3F006A77C2 /* Products */;
			projectDirPath = "";
			projectRoot = "";
			targets = (
				85DD38371D7ABA3F006A77C2 /* QiniuUploadDemo */,
				85DD38501D7ABA3F006A77C2 /* QiniuUploadDemoTests */,
				85DD385B1D7ABA3F006A77C2 /* QiniuUploadDemoUITests */,
			);
		};
/* End PBXProject section */

/* Begin PBXResourcesBuildPhase section */
		85DD38361D7ABA3F006A77C2 /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				858102361D7ABF1900B8FCB2 /* version.json in Resources */,
				85DD384B1D7ABA3F006A77C2 /* LaunchScreen.storyboard in Resources */,
				85DD38931D7ABC98006A77C2 /* ふつうのdisco.mp3 in Resources */,
				85DD38481D7ABA3F006A77C2 /* Assets.xcassets in Resources */,
				85DD38901D7ABC98006A77C2 /* test.jpg in Resources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		85DD384F1D7ABA3F006A77C2 /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				85DD38941D7ABC98006A77C2 /* ふつうのdisco.mp3 in Resources */,
				858102371D7ABF1900B8FCB2 /* version.json in Resources */,
				85DD38911D7ABC98006A77C2 /* test.jpg in Resources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		85DD385A1D7ABA3F006A77C2 /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				85DD38951D7ABC98006A77C2 /* ふつうのdisco.mp3 in Resources */,
				858102381D7ABF1900B8FCB2 /* version.json in Resources */,
				85DD38921D7ABC98006A77C2 /* test.jpg in Resources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXResourcesBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
		85DD38341D7ABA3F006A77C2 /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				858102301D7ABF1900B8FCB2 /* QiniuUploader.m in Sources */,
				8535ABB81D7B13350096FFE5 /* NSInputStream+POS.m in Sources */,
				8535ABBD1D7B13350096FFE5 /* POSFastAssetReader.m in Sources */,
				8535ABB91D7B13350096FFE5 /* POSAdjustedAssetReaderIOS7.m in Sources */,
				8535ABA01D7ACAE70096FFE5 /* QiniuMultipartElement.m in Sources */,
				8535ABB71D7B13350096FFE5 /* ALAssetsLibrary+POS.m in Sources */,
				8535ABBC1D7B13350096FFE5 /* POSBlobInputStreamAssetDataSource.m in Sources */,
				8535ABBA1D7B13350096FFE5 /* POSAdjustedAssetReaderIOS8.m in Sources */,
				858102331D7ABF1900B8FCB2 /* QUMBase64.m in Sources */,
				8581022A1D7ABF1900B8FCB2 /* QiniuFile.m in Sources */,
				85DD38431D7ABA3F006A77C2 /* ViewController.m in Sources */,
				8581022D1D7ABF1900B8FCB2 /* QiniuToken.m in Sources */,
				8535ABBB1D7B13350096FFE5 /* POSBlobInputStream.m in Sources */,
				85DD38401D7ABA3F006A77C2 /* AppDelegate.m in Sources */,
				85DD383D1D7ABA3F006A77C2 /* main.m in Sources */,
				8535AB9A1D7AC99A0096FFE5 /* QiniuInputStream.m in Sources */,
				8535ABBE1D7B13350096FFE5 /* POSLocking.m in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		85DD384D1D7ABA3F006A77C2 /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				858102311D7ABF1900B8FCB2 /* QiniuUploader.m in Sources */,
				858102341D7ABF1900B8FCB2 /* QUMBase64.m in Sources */,
				8581022B1D7ABF1900B8FCB2 /* QiniuFile.m in Sources */,
				8581022E1D7ABF1900B8FCB2 /* QiniuToken.m in Sources */,
				85DD38561D7ABA3F006A77C2 /* QiniuUploadDemoTests.m in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		85DD38581D7ABA3F006A77C2 /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				858102321D7ABF1900B8FCB2 /* QiniuUploader.m in Sources */,
				858102351D7ABF1900B8FCB2 /* QUMBase64.m in Sources */,
				8581022C1D7ABF1900B8FCB2 /* QiniuFile.m in Sources */,
				8581022F1D7ABF1900B8FCB2 /* QiniuToken.m in Sources */,
				85DD38611D7ABA3F006A77C2 /* QiniuUploadDemoUITests.m in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXSourcesBuildPhase section */

/* Begin PBXTargetDependency section */
		85DD38531D7ABA3F006A77C2 /* PBXTargetDependency */ = {
			isa = PBXTargetDependency;
			target = 85DD38371D7ABA3F006A77C2 /* QiniuUploadDemo */;
			targetProxy = 85DD38521D7ABA3F006A77C2 /* PBXContainerItemProxy */;
		};
		85DD385E1D7ABA3F006A77C2 /* PBXTargetDependency */ = {
			isa = PBXTargetDependency;
			target = 85DD38371D7ABA3F006A77C2 /* QiniuUploadDemo */;
			targetProxy = 85DD385D1D7ABA3F006A77C2 /* PBXContainerItemProxy */;
		};
/* End PBXTargetDependency section */

/* Begin PBXVariantGroup section */
		85DD38491D7ABA3F006A77C2 /* LaunchScreen.storyboard */ = {
			isa = PBXVariantGroup;
			children = (
				85DD384A1D7ABA3F006A77C2 /* Base */,
			);
			name = LaunchScreen.storyboard;
			sourceTree = "<group>";
		};
/* End PBXVariantGroup section */

/* Begin XCBuildConfiguration section */
		85DD38631D7ABA3F006A77C2 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
				COPY_PHASE_STRIP = NO;
				DEBUG_INFORMATION_FORMAT = dwarf;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				ENABLE_TESTABILITY = YES;
				GCC_C_LANGUAGE_STANDARD = gnu99;
				GCC_DYNAMIC_NO_PIC = NO;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_OPTIMIZATION_LEVEL = 0;
				GCC_PREPROCESSOR_DEFINITIONS = (
					"DEBUG=1",
					"$(inherited)",
				);
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				IPHONEOS_DEPLOYMENT_TARGET = 9.3;
				MTL_ENABLE_DEBUG_INFO = YES;
				ONLY_ACTIVE_ARCH = YES;
				SDKROOT = iphoneos;
				TARGETED_DEVICE_FAMILY = "1,2";
			};
			name = Debug;
		};
		85DD38641D7ABA3F006A77C2 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
				COPY_PHASE_STRIP = NO;
				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
				ENABLE_NS_ASSERTIONS = NO;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				GCC_C_LANGUAGE_STANDARD = gnu99;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				IPHONEOS_DEPLOYMENT_TARGET = 9.3;
				MTL_ENABLE_DEBUG_INFO = NO;
				SDKROOT = iphoneos;
				TARGETED_DEVICE_FAMILY = "1,2";
				VALIDATE_PRODUCT = YES;
			};
			name = Release;
		};
		85DD38661D7ABA3F006A77C2 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
				DEVELOPMENT_TEAM = 5M34M6CU7U;
				INFOPLIST_FILE = QiniuUploadDemo/Info.plist;
				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
				PRODUCT_BUNDLE_IDENTIFIER = "zach-hu.QiniuUploadDemo";
				PRODUCT_NAME = "$(TARGET_NAME)";
			};
			name = Debug;
		};
		85DD38671D7ABA3F006A77C2 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				DEVELOPMENT_TEAM = 5M34M6CU7U;
				INFOPLIST_FILE = QiniuUploadDemo/Info.plist;
				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
				PRODUCT_BUNDLE_IDENTIFIER = "zach-hu.QiniuUploadDemo";
				PRODUCT_NAME = "$(TARGET_NAME)";
			};
			name = Release;
		};
		85DD38691D7ABA3F006A77C2 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				BUNDLE_LOADER = "$(TEST_HOST)";
				INFOPLIST_FILE = QiniuUploadDemoTests/Info.plist;
				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
				PRODUCT_BUNDLE_IDENTIFIER = "zach-hu.QiniuUploadDemoTests";
				PRODUCT_NAME = "$(TARGET_NAME)";
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/QiniuUploadDemo.app/QiniuUploadDemo";
			};
			name = Debug;
		};
		85DD386A1D7ABA3F006A77C2 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				BUNDLE_LOADER = "$(TEST_HOST)";
				INFOPLIST_FILE = QiniuUploadDemoTests/Info.plist;
				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
				PRODUCT_BUNDLE_IDENTIFIER = "zach-hu.QiniuUploadDemoTests";
				PRODUCT_NAME = "$(TARGET_NAME)";
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/QiniuUploadDemo.app/QiniuUploadDemo";
			};
			name = Release;
		};
		85DD386C1D7ABA3F006A77C2 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				INFOPLIST_FILE = QiniuUploadDemoUITests/Info.plist;
				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
				PRODUCT_BUNDLE_IDENTIFIER = "zach-hu.QiniuUploadDemoUITests";
				PRODUCT_NAME = "$(TARGET_NAME)";
				TEST_TARGET_NAME = QiniuUploadDemo;
			};
			name = Debug;
		};
		85DD386D1D7ABA3F006A77C2 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				INFOPLIST_FILE = QiniuUploadDemoUITests/Info.plist;
				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
				PRODUCT_BUNDLE_IDENTIFIER = "zach-hu.QiniuUploadDemoUITests";
				PRODUCT_NAME = "$(TARGET_NAME)";
				TEST_TARGET_NAME = QiniuUploadDemo;
			};
			name = Release;
		};
/* End XCBuildConfiguration section */

/* Begin XCConfigurationList section */
		85DD38331D7ABA3F006A77C2 /* Build configuration list for PBXProject "QiniuUploadDemo" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				85DD38631D7ABA3F006A77C2 /* Debug */,
				85DD38641D7ABA3F006A77C2 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		85DD38651D7ABA3F006A77C2 /* Build configuration list for PBXNativeTarget "QiniuUploadDemo" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				85DD38661D7ABA3F006A77C2 /* Debug */,
				85DD38671D7ABA3F006A77C2 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		85DD38681D7ABA3F006A77C2 /* Build configuration list for PBXNativeTarget "QiniuUploadDemoTests" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				85DD38691D7ABA3F006A77C2 /* Debug */,
				85DD386A1D7ABA3F006A77C2 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		85DD386B1D7ABA3F006A77C2 /* Build configuration list for PBXNativeTarget "QiniuUploadDemoUITests" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				85DD386C1D7ABA3F006A77C2 /* Debug */,
				85DD386D1D7ABA3F006A77C2 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
/* End XCConfigurationList section */
	};
	rootObject = 85DD38301D7ABA3F006A77C2 /* Project object */;
}


================================================
FILE: QiniuUploadDemo/QiniuUploadDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "self:QiniuUploadDemo.xcodeproj">
   </FileRef>
</Workspace>


================================================
FILE: QiniuUploadDemo/QiniuUploadDemoTests/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>BNDL</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>
	<key>CFBundleSignature</key>
	<string>????</string>
	<key>CFBundleVersion</key>
	<string>1</string>
</dict>
</plist>


================================================
FILE: QiniuUploadDemo/QiniuUploadDemoTests/QiniuUploadDemoTests.m
================================================
//
//  QiniuUploadDemoTests.m
//  QiniuUploadDemoTests
//
//  Created by 胡 桓铭 on 16/9/3.
//  Copyright © 2016年 胡 桓铭. All rights reserved.
//

#import <XCTest/XCTest.h>

@interface QiniuUploadDemoTests : XCTestCase

@end

@implementation QiniuUploadDemoTests

- (void)setUp {
    [super setUp];
    // Put setup code here. This method is called before the invocation of each test method in the class.
}

- (void)tearDown {
    // Put teardown code here. This method is called after the invocation of each test method in the class.
    [super tearDown];
}

- (void)testExample {
    // This is an example of a functional test case.
    // Use XCTAssert and related functions to verify your tests produce the correct results.
}

- (void)testPerformanceExample {
    // This is an example of a performance test case.
    [self measureBlock:^{
        // Put the code you want to measure the time of here.
    }];
}

@end


================================================
FILE: QiniuUploadDemo/QiniuUploadDemoUITests/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>BNDL</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>
	<key>CFBundleSignature</key>
	<string>????</string>
	<key>CFBundleVersion</key>
	<string>1</string>
</dict>
</plist>


================================================
FILE: QiniuUploadDemo/QiniuUploadDemoUITests/QiniuUploadDemoUITests.m
================================================
//
//  QiniuUploadDemoUITests.m
//  QiniuUploadDemoUITests
//
//  Created by 胡 桓铭 on 16/9/3.
//  Copyright © 2016年 胡 桓铭. All rights reserved.
//

#import <XCTest/XCTest.h>

@interface QiniuUploadDemoUITests : XCTestCase

@end

@implementation QiniuUploadDemoUITests

- (void)setUp {
    [super setUp];
    
    // Put setup code here. This method is called before the invocation of each test method in the class.
    
    // In UI tests it is usually best to stop immediately when a failure occurs.
    self.continueAfterFailure = NO;
    // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
    [[[XCUIApplication alloc] init] launch];
    
    // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
}

- (void)tearDown {
    // Put teardown code here. This method is called after the invocation of each test method in the class.
    [super tearDown];
}

- (void)testExample {
    // Use recording to get started writing UI tests.
    // Use XCTAssert and related functions to verify your tests produce the correct results.
}

@end


================================================
FILE: README.md
================================================
# QiniuUpload

[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/QiniuUpload.svg)](https://img.shields.io/cocoapods/v/QiniuUpload.svg)
[![Platform](https://img.shields.io/cocoapods/p/QiniuUpload.svg?style=flat)](http://cocoadocs.org/docsets/QiniuUpload)

qiniu_upload 是一款支持七牛云存储的 iOS/macOS sdk。

qiniu_upload 除了文件上传等基本功能完,还实现了多文件队列上传。

qiniu_upload 转入了维护,想去写个 UploadKit。

## Done

- [x] 减小内存占用,清除内存泄露
- [x] 支持多种数据来源,包括 ALAsset, NSData,NSFileManager
- [x] 支持 NSInputStream 方式上传
- [x] 支持并发上传
- [x] 支持版本更新在开发环境中提示 
- [x] Remove all warnings 
- [x] Remove AFNetWorking support

## 如何开始

---

### CocoaPods

#### Podfile

```shell
    pod "QiniuUpload"
```

### 手动安装

复制Classes目录下的类到工程项目中就行了。

### 开始编码

#### QiniuToken

首先要初始化一个 QiniuToken。scope, secretKey, accessKey 注册七牛后官方都会给出

```Objective-C
    [QiniuToken registerWithScope:@"your_scope" SecretKey:@"your_secretKey" Accesskey:@"your_accesskey"];
```

这样初始化,一个 Token 的默认有效生命周期是5分钟,如果你想自定义生命周期的话,可以这样初始化

```Objective-C
    [QiniuToken registerWithScope:@"your_scope" SecretKey:@"your_secretKey" Accesskey:@"your_accesskey" TimeToLive:60]
```

生成一个上传凭证

```Objective-C
    NSString *uploadToken = [[QiniuToken sharedQiniuToken] uploadToken]
```

不推荐在生产环境的代码中直接填写 accesskey 和 secretKey 来使用。

#### QiniuFile

初始化要上传的七牛文件,图片,音频都可以。

以图片为例「NSData 方式」

```Objective-C
    QiniuFile *file = [[QiniuFile alloc] initWithFileData:UIImageJPEGRepresentation(your_image, 1.0f)];
```

或者一段音频「路径方式」

```Objective-C
    NSString *path = [NSString stringWithFormat:@"%@/%@",[NSBundle mainBundle].resourcePath,@"your_mp3"];
    QiniuFile *file = [[QiniuFile alloc] initWithPath:path];
```

或者 ALAsset

```Objective-C
    QiniuFile *file = [[QiniuFile alloc] initWithAsset: your_asset]];
```

#### QiniuUploader

#### 添加文件

```Objective-C
    uploader.files = @[file, file, file];
```

这里的 QinniuFile 可以部分是图片,部分是视频、音频,不会对上传有任何影响。

### 设置并发上限

```Objective-C
    [[QiniuUploader sharedUploader] setMaxConcurrentNumber:3];
```

建议不要设置过大,因为 iOS 的连接数是有限的。

#### 开始上传

上面乱七八糟的设置完了后,就调用这个开始上传。

```Objective-C
    [uploader startUpload:uploadToken uploadOneFileSucceededHandler:^(NSInteger index, NSDictionary * _Nonnull info) {
            NSLog(@"index: %ld info: %@",(long)index, info);
        } uploadOneFileFailedHandler:^(NSInteger index, NSError * _Nullable error) {
            NSLog(@"error: %@", error);
        } uploadOneFileProgressHandler:^(NSInteger index, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) {
             NSLog(@"index:%ld percent:%f",(long)index, totalBytesSent * 1.0 / totalBytesExpectedToSend);
        } uploadAllFilesComplete:^{
            NSLog(@"complete");
        }];
```

再次提醒,不要在 App 内直接写入 key。

#### 取消全部上传任务

当你希望取消掉所有上传任务时

```Objective-C
    [uploader cancelAllUploadTask]
```

## 最后

如果还有不清楚的地方, 可以看看 QiniuUploadDemo,里面什么都有。。。

如果你有希望加入的特性,可以在 issue 在留言。
最后无耻的求个s
Download .txt
gitextract_ev4nzkd6/

├── CHANGELOG.md
├── Classes/
│   ├── POSInputStreamLibrary/
│   │   ├── ALAssetsLibrary+POS.h
│   │   ├── ALAssetsLibrary+POS.m
│   │   ├── NSInputStream+POS.h
│   │   ├── NSInputStream+POS.m
│   │   ├── POSAdjustedAssetReaderIOS7.h
│   │   ├── POSAdjustedAssetReaderIOS7.m
│   │   ├── POSAdjustedAssetReaderIOS8.h
│   │   ├── POSAdjustedAssetReaderIOS8.m
│   │   ├── POSAssetReader.h
│   │   ├── POSBlobInputStream-Prefix.pch
│   │   ├── POSBlobInputStream.h
│   │   ├── POSBlobInputStream.m
│   │   ├── POSBlobInputStreamAssetDataSource.h
│   │   ├── POSBlobInputStreamAssetDataSource.m
│   │   ├── POSBlobInputStreamDataSource.h
│   │   ├── POSFastAssetReader.h
│   │   ├── POSFastAssetReader.m
│   │   ├── POSInputStreamLibrary-Prefix.pch
│   │   ├── POSInputStreamLibrary.h
│   │   ├── POSLocking.h
│   │   └── POSLocking.m
│   ├── QUMBase64.h
│   ├── QUMBase64.m
│   ├── QUMDefines.h
│   ├── QiniuFile.h
│   ├── QiniuFile.m
│   ├── QiniuInputStream.h
│   ├── QiniuInputStream.m
│   ├── QiniuMultipartElement.h
│   ├── QiniuMultipartElement.m
│   ├── QiniuToken.h
│   ├── QiniuToken.m
│   ├── QiniuUploader.h
│   ├── QiniuUploader.m
│   └── version.json
├── LICENSE
├── QiniuUpload.podspec
├── QiniuUploadDemo/
│   ├── .gitignore
│   ├── QiniuUploadDemo/
│   │   ├── AppDelegate.h
│   │   ├── AppDelegate.m
│   │   ├── Assets.xcassets/
│   │   │   └── AppIcon.appiconset/
│   │   │       └── Contents.json
│   │   ├── Base.lproj/
│   │   │   ├── LaunchScreen.storyboard
│   │   │   └── Main.storyboard
│   │   ├── Info.plist
│   │   ├── ViewController.h
│   │   ├── ViewController.m
│   │   └── main.m
│   ├── QiniuUploadDemo.xcodeproj/
│   │   ├── project.pbxproj
│   │   └── project.xcworkspace/
│   │       └── contents.xcworkspacedata
│   ├── QiniuUploadDemoTests/
│   │   ├── Info.plist
│   │   └── QiniuUploadDemoTests.m
│   └── QiniuUploadDemoUITests/
│       ├── Info.plist
│       └── QiniuUploadDemoUITests.m
└── README.md
Download .txt
SYMBOL INDEX (7 symbols across 2 files)

FILE: Classes/POSInputStreamLibrary/POSAssetReader.h
  type POSLength (line 13) | typedef long long POSLength;

FILE: Classes/QUMDefines.h
  type NSInteger (line 182) | typedef long NSInteger;
  type NSUInteger (line 183) | typedef unsigned long NSUInteger;
  type NSInteger (line 185) | typedef int NSInteger;
  type NSUInteger (line 186) | typedef unsigned int NSUInteger;
  type CGFloat (line 197) | typedef double CGFloat;
  type CGFloat (line 202) | typedef float CGFloat;
Condensed preview — 55 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (207K chars).
[
  {
    "path": "CHANGELOG.md",
    "chars": 1530,
    "preview": "# CHANGELOG\n\n## x.x.x Release notes (yyyy-MM-dd)\n\n### API breaking changes\n\n### Enhancements\n\n### Bugfixes\n\n## 3.0.0 Rel"
  },
  {
    "path": "Classes/POSInputStreamLibrary/ALAssetsLibrary+POS.h",
    "chars": 502,
    "preview": "//\n//  ALAssetsLibrary+POS.h\n//  POSInputStreamLibrary\n//\n//  Created by Osipov on 31.08.15.\n//  Copyright (c) 2015 Pave"
  },
  {
    "path": "Classes/POSInputStreamLibrary/ALAssetsLibrary+POS.m",
    "chars": 1673,
    "preview": "//\n//  ALAssetsLibrary+POS.m\n//  POSInputStreamLibrary\n//\n//  Created by Osipov on 31.08.15.\n//  Copyright (c) 2015 Pave"
  },
  {
    "path": "Classes/POSInputStreamLibrary/NSInputStream+POS.h",
    "chars": 566,
    "preview": "//\n//  NSInputStream+POS.h\n//  POSBlobInputStreamLibrary\n//\n//  Created by Pavel Osipov on 17.07.13.\n//  Copyright (c) 2"
  },
  {
    "path": "Classes/POSInputStreamLibrary/NSInputStream+POS.m",
    "chars": 1848,
    "preview": "//\n//  NSInputStream+POS.m\n//  POSBlobInputStreamLibrary\n//\n//  Created by Pavel Osipov on 17.07.13.\n//  Copyright (c) 2"
  },
  {
    "path": "Classes/POSInputStreamLibrary/POSAdjustedAssetReaderIOS7.h",
    "chars": 534,
    "preview": "//\n//  POSAdjustedAssetReaderIOS7.h\n//  POSInputStreamLibrary\n//\n//  Created by Pavel Osipov on 12.05.15.\n//  Copyright "
  },
  {
    "path": "Classes/POSInputStreamLibrary/POSAdjustedAssetReaderIOS7.m",
    "chars": 6509,
    "preview": "//\n//  POSAdjustedAssetReaderIOS7.m\n//  POSInputStreamLibrary\n//\n//  Created by Pavel Osipov on 12.05.15.\n//  Copyright "
  },
  {
    "path": "Classes/POSInputStreamLibrary/POSAdjustedAssetReaderIOS8.h",
    "chars": 975,
    "preview": "//\n//  POSAdjustedAssetReaderIOS8.h\n//  POSInputStreamLibrary\n//\n//  Created by Pavel Osipov on 12.05.15.\n//  Copyright "
  },
  {
    "path": "Classes/POSInputStreamLibrary/POSAdjustedAssetReaderIOS8.m",
    "chars": 5092,
    "preview": "//\n//  POSAdjustedAssetReaderIOS8.m\n//  POSInputStreamLibrary\n//\n//  Created by Pavel Osipov on 12.05.15.\n//  Copyright "
  },
  {
    "path": "Classes/POSInputStreamLibrary/POSAssetReader.h",
    "chars": 778,
    "preview": "//\n//  POSAssetReader.h\n//  POSInputStreamLibrary\n//\n//  Created by Pavel Osipov on 08.05.15.\n//  Copyright (c) 2015 Pav"
  },
  {
    "path": "Classes/POSInputStreamLibrary/POSBlobInputStream-Prefix.pch",
    "chars": 182,
    "preview": "//\n//  Prefix header\n//\n//  The contents of this file are implicitly included at the beginning of every source file.\n//\n"
  },
  {
    "path": "Classes/POSInputStreamLibrary/POSBlobInputStream.h",
    "chars": 663,
    "preview": "//\n//  POSBlobInputStream.h\n//  POSBlobInputStreamLibrary\n//\n//  Created by Pavel Osipov on 02.07.13.\n//  Copyright (c) "
  },
  {
    "path": "Classes/POSInputStreamLibrary/POSBlobInputStream.m",
    "chars": 14679,
    "preview": "//\n//  POSBlobInputStream.m\n//  POSBlobInputStreamLibrary\n//\n//  Created by Pavel Osipov on 02.07.13.\n//  Copyright (c) "
  },
  {
    "path": "Classes/POSInputStreamLibrary/POSBlobInputStreamAssetDataSource.h",
    "chars": 3398,
    "preview": "//\n//  POSBlobInputStreamAssetDataSource.h\n//  POSInputStreamLibrary\n//\n//  Created by Pavel Osipov on 16.07.13.\n//  Cop"
  },
  {
    "path": "Classes/POSInputStreamLibrary/POSBlobInputStreamAssetDataSource.m",
    "chars": 9897,
    "preview": "//\n//  POSBlobInputStreamAssetDataSource.m\n//  POSInputStreamLibrary\n//\n//  Created by Osipov on 06.05.15.\n//  Copyright"
  },
  {
    "path": "Classes/POSInputStreamLibrary/POSBlobInputStreamDataSource.h",
    "chars": 1336,
    "preview": "//\n//  POSBlobInputStreamDataSource.h\n//  POSBlobInputStreamLibrary\n//\n//  Created by Pavel Osipov on 16.07.13.\n//  Copy"
  },
  {
    "path": "Classes/POSInputStreamLibrary/POSFastAssetReader.h",
    "chars": 252,
    "preview": "//\n//  POSFastAssetReader.h\n//  POSInputStreamLibrary\n//\n//  Created by Pavel Osipov on 08.05.15.\n//  Copyright (c) 2015"
  },
  {
    "path": "Classes/POSInputStreamLibrary/POSFastAssetReader.m",
    "chars": 3177,
    "preview": "//\n//  POSFastAssetReader.m\n//  POSInputStreamLibrary\n//\n//  Created by Pavel Osipov on 08.05.15.\n//  Copyright (c) 2015"
  },
  {
    "path": "Classes/POSInputStreamLibrary/POSInputStreamLibrary-Prefix.pch",
    "chars": 182,
    "preview": "//\n//  Prefix header\n//\n//  The contents of this file are implicitly included at the beginning of every source file.\n//\n"
  },
  {
    "path": "Classes/POSInputStreamLibrary/POSInputStreamLibrary.h",
    "chars": 265,
    "preview": "//\n//  POSInputStreamLibrary.h\n//  POSInputStreamLibrary\n//\n//  Created by Osipov on 27.02.14.\n//  Copyright (c) 2014 Pa"
  },
  {
    "path": "Classes/POSInputStreamLibrary/POSLocking.h",
    "chars": 375,
    "preview": "//\n//  POSLocking.h\n//  POSInputStreamLibrary\n//\n//  Created by Osipov on 06.05.15.\n//  Copyright (c) 2015 Pavel Osipov."
  },
  {
    "path": "Classes/POSInputStreamLibrary/POSLocking.m",
    "chars": 622,
    "preview": "//\n//  POSLocking.m\n//  POSInputStreamLibrary\n//\n//  Created by Osipov on 06.05.15.\n//  Copyright (c) 2015 Pavel Osipov."
  },
  {
    "path": "Classes/QUMBase64.h",
    "chars": 5616,
    "preview": "//\n//  QUMBase64.h\n//  Moto\n//\n//  Created by 胡 桓铭 on 14-2-17.\n//  Copyright (c) 2014年 agile. All rights reserved.\n//\n\n/"
  },
  {
    "path": "Classes/QUMBase64.m",
    "chars": 26942,
    "preview": "//\n//  QUMBase64.m\n//  Moto\n//\n//  Created by 胡 桓铭 on 14-2-17.\n//  Copyright (c) 2014年 agile. All rights reserved.\n//\n\n/"
  },
  {
    "path": "Classes/QUMDefines.h",
    "chars": 7897,
    "preview": "//\n// QUMDefines.h\n//\n//  Copyright 2008 Google Inc.\n//\n//  Licensed under the Apache License, Version 2.0 (the \"License"
  },
  {
    "path": "Classes/QiniuFile.h",
    "chars": 1261,
    "preview": "//\n//  QiniuFile.h\n//  QiniuUploadDemo\n//\n//  Created by 胡 桓铭 on 14-5-18.\n//  Copyright (c) 2014年 hu. All rights reserve"
  },
  {
    "path": "Classes/QiniuFile.m",
    "chars": 778,
    "preview": "//\n//  QiniuFile.m\n//  QiniuUploadDemo\n//\n//  Created by 胡 桓铭 on 14-5-18.\n//  Copyright (c) 2014年 hu. All rights reserve"
  },
  {
    "path": "Classes/QiniuInputStream.h",
    "chars": 1238,
    "preview": "//\n//  QiniuInputStream.h\n//  QiniuUploadDemo\n//\n//  Created by 胡 桓铭 on 16/9/3.\n//  Copyright © 2016年 胡 桓铭. All rights r"
  },
  {
    "path": "Classes/QiniuInputStream.m",
    "chars": 5086,
    "preview": "//\n//  QiniuInputStream.m\n//  QiniuUploadDemo\n//\n//  Created by 胡 桓铭 on 16/9/3.\n//  Copyright © 2016年 胡 桓铭. All rights r"
  },
  {
    "path": "Classes/QiniuMultipartElement.h",
    "chars": 1866,
    "preview": "//\n//  QiniuMultipartElement.h\n//  QiniuUploadDemo\n//\n//  Created by 胡 桓铭 on 16/9/3.\n//  Copyright © 2016年 胡 桓铭. All rig"
  },
  {
    "path": "Classes/QiniuMultipartElement.m",
    "chars": 7291,
    "preview": "//\n//  QiniuMultipartElement.m\n//  QiniuUploadDemo\n//\n//  Created by 胡 桓铭 on 16/9/3.\n//  Copyright © 2016年 胡 桓铭. All rig"
  },
  {
    "path": "Classes/QiniuToken.h",
    "chars": 1435,
    "preview": "//\n//  QiniuToken.h\n//  QiniuUploadDemo\n//\n//  Created by 胡 桓铭 on 14-5-17.\n//  Copyright (c) 2014年 hu. All rights reserv"
  },
  {
    "path": "Classes/QiniuToken.m",
    "chars": 3881,
    "preview": "//\n//  QiniuToken.m\n//  QiniuUploadDemo\n//\n//  Created by 胡 桓铭 on 14-5-17.\n//  Copyright (c) 2014年 hu. All rights reserv"
  },
  {
    "path": "Classes/QiniuUploader.h",
    "chars": 1339,
    "preview": "//\n//  QiniuUploader.h\n//  QiniuUploadDemo\n//\n//  Created by 胡 桓铭 on 14-5-17.\n//  Copyright (c) 2014年 hu. All rights res"
  },
  {
    "path": "Classes/QiniuUploader.m",
    "chars": 9852,
    "preview": "//\n//  QiniuUploader.m\n//  QiniuUploadDemo\n//\n//  Created by 胡 桓铭 on 14-5-17.\n//  Copyright (c) 2014年 hu. All rights res"
  },
  {
    "path": "Classes/version.json",
    "chars": 124,
    "preview": "{\n  \"versionName\":\"3.0.0\",\n  \"version\":16,\n  \"desc\":\"https://github.com/huhuanming/qiniu_upload/blob/master/CHANGELOG.md"
  },
  {
    "path": "LICENSE",
    "chars": 1076,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2014 huhuanming\n\nPermission is hereby granted, free of charge, to any person obtain"
  },
  {
    "path": "QiniuUpload.podspec",
    "chars": 1220,
    "preview": "Pod::Spec.new do |s|\n\n  s.name         = \"QiniuUpload\"\n  s.version      = \"3.0.0\"\n  s.summary      = \"支持批量上传的七牛上传sdk,音频、"
  },
  {
    "path": "QiniuUploadDemo/.gitignore",
    "chars": 429,
    "preview": "\n# Created by https://www.gitignore.io/api/xcode\n\n### Xcode ###\n# Xcode\n#\n# gitignore contributors: remember to update G"
  },
  {
    "path": "QiniuUploadDemo/QiniuUploadDemo/AppDelegate.h",
    "chars": 271,
    "preview": "//\n//  AppDelegate.h\n//  QiniuUploadDemo\n//\n//  Created by 胡 桓铭 on 16/9/3.\n//  Copyright © 2016年 胡 桓铭. All rights reserv"
  },
  {
    "path": "QiniuUploadDemo/QiniuUploadDemo/AppDelegate.m",
    "chars": 2174,
    "preview": "//\n//  AppDelegate.m\n//  QiniuUploadDemo\n//\n//  Created by 胡 桓铭 on 16/9/3.\n//  Copyright © 2016年 胡 桓铭. All rights reserv"
  },
  {
    "path": "QiniuUploadDemo/QiniuUploadDemo/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "chars": 1495,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"iphone\",\n      \"size\" : \"20x20\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\""
  },
  {
    "path": "QiniuUploadDemo/QiniuUploadDemo/Base.lproj/LaunchScreen.storyboard",
    "chars": 1664,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard"
  },
  {
    "path": "QiniuUploadDemo/QiniuUploadDemo/Base.lproj/Main.storyboard",
    "chars": 1575,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard"
  },
  {
    "path": "QiniuUploadDemo/QiniuUploadDemo/Info.plist",
    "chars": 1599,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "QiniuUploadDemo/QiniuUploadDemo/ViewController.h",
    "chars": 249,
    "preview": "//\n//  ViewController.h\n//  QiniuUploadDemo\n//\n//  Created by 胡 桓铭 on 16/9/3.\n//  Copyright © 2016年 胡 桓铭. All rights res"
  },
  {
    "path": "QiniuUploadDemo/QiniuUploadDemo/ViewController.m",
    "chars": 6909,
    "preview": "//\n//  ViewController.m\n//  QiniuUploadDemo\n//\n//  Created by 胡 桓铭 on 16/9/3.\n//  Copyright © 2016年 胡 桓铭. All rights res"
  },
  {
    "path": "QiniuUploadDemo/QiniuUploadDemo/main.m",
    "chars": 328,
    "preview": "//\n//  main.m\n//  QiniuUploadDemo\n//\n//  Created by 胡 桓铭 on 16/9/3.\n//  Copyright © 2016年 胡 桓铭. All rights reserved.\n//\n"
  },
  {
    "path": "QiniuUploadDemo/QiniuUploadDemo.xcodeproj/project.pbxproj",
    "chars": 36498,
    "preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
  },
  {
    "path": "QiniuUploadDemo/QiniuUploadDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "chars": 160,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:QiniuUploadDemo"
  },
  {
    "path": "QiniuUploadDemo/QiniuUploadDemoTests/Info.plist",
    "chars": 733,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "QiniuUploadDemo/QiniuUploadDemoTests/QiniuUploadDemoTests.m",
    "chars": 917,
    "preview": "//\n//  QiniuUploadDemoTests.m\n//  QiniuUploadDemoTests\n//\n//  Created by 胡 桓铭 on 16/9/3.\n//  Copyright © 2016年 胡 桓铭. All"
  },
  {
    "path": "QiniuUploadDemo/QiniuUploadDemoUITests/Info.plist",
    "chars": 733,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "QiniuUploadDemo/QiniuUploadDemoUITests/QiniuUploadDemoUITests.m",
    "chars": 1224,
    "preview": "//\n//  QiniuUploadDemoUITests.m\n//  QiniuUploadDemoUITests\n//\n//  Created by 胡 桓铭 on 16/9/3.\n//  Copyright © 2016年 胡 桓铭."
  },
  {
    "path": "README.md",
    "chars": 3158,
    "preview": "# QiniuUpload\n\n[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/QiniuUpload.svg)](https://img.shields.io/coco"
  }
]

About this extraction

This page contains the full source code of the huhuanming/qiniu_upload GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 55 files (187.6 KB), approximately 53.9k tokens, and a symbol index with 7 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!