解决了语音上传到问题,接下来要解决下载播放问题

This commit is contained in:
joywayer
2025-06-15 12:36:47 +08:00
parent bba3ed1cb4
commit c11fc62bf1
513 changed files with 31197 additions and 2969 deletions

View File

@@ -43,6 +43,8 @@
#import <CommonCrypto/CommonDigest.h>
#import <AdSupport/ASIdentifierManager.h>
#import "XianliaoApiManager.h"
#import "QiniuManager.h"
#import "QiniuConfig.h"
@interface gameController ()
<WKNavigationDelegate,WXApiManagerDelegate,VoiceRecorderBaseVCDelegate,AVAudioPlayerDelegate,ASIHTTPRequestDelegate,AMapLocationManagerDelegate,UIActionSheetDelegate,AgoraRtcEngineDelegate>
{
@@ -844,7 +846,7 @@
CTTelephonyNetworkInfo *telephonyInfo = [[CTTelephonyNetworkInfo alloc] init];
CTCarrier *carrier = [telephonyInfo subscriberCellularProvider];
NSString *currentCountry=[carrier carrierName];
// NSLog(@"[carrier isoCountryCode]==%@,[carrier allowsVOIP]=%d,[carrier mobileCountryCode=%@,[carrier mobileCountryCode]=%@",[carrier isoCountryCode],[carrier allowsVOIP],[carrier mobileCountryCode],[carrier mobileNetworkCode]);
// NSLog(@"[carrier isoCountryCode]==%@,[carrier allowsVOIP]=%d,[carrier mobileCountryCode=%@,[carrier mobileNetworkCode]=%@",[carrier isoCountryCode],[carrier allowsVOIP],[carrier mobileCountryCode],[carrier mobileNetworkCode]);
UIDevice *device = [[UIDevice alloc] init];
// NSString *name = device.name; //
NSString *model = device.model; //
@@ -2194,6 +2196,15 @@
NSLog(@"录音时长过短,可能无效: %.2f秒", interval);
return;
}
//4.
if (play == nil)
{
NSLog(@"ERror creating player: %@", [play description]);
}else{
[play play];
}
NSString *amrPath = [FuncPublic GetPathByFileName:_fileName ofType:@"amr"];
NSLog(@"尝试转换WAV到AMR源文件: %@,目标文件: %@", _filePath, amrPath);
@@ -2223,23 +2234,67 @@
NSLog(@"转换WAV到AMR成功");
NSData *amrData = [NSData dataWithContentsOfFile:amrPath];
if (amrData && amrData.length > 0) {
//
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
//text/html
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json",
@"text/html",
@"image/jpeg",
@"image/png",
@"application/octet-stream",
@"text/json",
@"audio/amr",
nil];
NSString *string=@"http://gameapi.0791ts.cn/api/UpLoad/PostFile";
// 使QiniuManager
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat =@"yyyyMMddHHmmss";
NSString *str = [formatter stringFromDate:[NSDate date]];
NSString *fileName = [NSString stringWithFormat:@"%@%08X.amr", str, arc4random()];
// ...
formatter.dateFormat = @"yyyyMMddHHmmss";
NSString *timeStr = [formatter stringFromDate:[NSDate date]];
NSString *uniqueFileName = [NSString stringWithFormat:@"%@_%08X.amr", timeStr, arc4random()];
//
// UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示"
// message:@"正在上传录音文件..."
// preferredStyle:UIAlertControllerStyleAlert];
// [self presentViewController:alertController animated:YES completion:nil];
//
[[QiniuManager sharedManager] uploadAudioFile:amrPath
fileName:uniqueFileName
progressHandler:^(float progress) {
//
// dispatch_async(dispatch_get_main_queue(), ^{
// alertController.message = [NSString stringWithFormat:@"正在上传录音文件... %.0f%%", progress * 100];
// });
}
completionHandler:^(NSString *key, NSError *error) {
//
dispatch_async(dispatch_get_main_queue(), ^{
// [alertController dismissViewControllerAnimated:YES completion:nil];
if (error) {
//
// UIAlertController *errorAlert = [UIAlertController alertControllerWithTitle:@"上传失败"
// message:error.localizedDescription
// preferredStyle:UIAlertControllerStyleAlert];
// UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil];
// [errorAlert addAction:okAction];
// [self presentViewController:errorAlert animated:YES completion:nil];
//
NSLog(@"七牛云上传失败: %@", error);
} else {
// URL
NSString *fileUrl = [[QiniuManager sharedManager] getFileUrlWithKey:key];
NSString *audiourl=[NSString stringWithFormat:@"%@/%@",kQiniuDomain,uniqueFileName];
[_bridge callHandler:@"getaudiourl" data:@{ @"audiourl":audiourl,@"time":[NSString stringWithFormat:@"%ld",(long)time]} ];
NSLog(@"七牛云上传成功文件URL: %@", fileUrl);
// URLJS
if (self->_bridge) {
[self->_bridge callHandler:@"recordSuccess" data:@{
@"fileUrl": fileUrl,
@"fileName": uniqueFileName,
@"fileKey": key
}];
}
}
});
}];
//
} else {
NSLog(@"AMR文件无效或为空");
}
@@ -2476,3 +2531,5 @@
}}
@end

View File

@@ -0,0 +1,25 @@
//
// QiniuConfig.h
// msext
//
// Created on June 15, 2025.
//
#ifndef QiniuConfig_h
#define QiniuConfig_h
/* 七牛云相关配置常量 */
// AccessKey 和 SecretKey从七牛云控制台获取
extern NSString *const kQiniuAccessKey;
extern NSString *const kQiniuSecretKey;
// 存储空间名称
extern NSString *const kQiniuBucketName;
// 七牛云默认域名
extern NSString *const kQiniuDomain;
// 录音文件目录
extern NSString *const kQiniuRecordingDirectory;
#endif /* QiniuConfig_h */

View File

@@ -0,0 +1,22 @@
//
// QiniuConfig.m
// msext
//
// Created on June 15, 2025.
//
#import <Foundation/Foundation.h>
#import "QiniuConfig.h"
// AccessKey SecretKey
NSString *const kQiniuAccessKey = @"dQbQLUm1jIuL9PEq4jd6VKB-6pPxPEdg7le9KeBm";
NSString *const kQiniuSecretKey = @"RCZpwLhAPoQ2sQQyWXzMJc7Po2MyZWfUJeW4Jmfq";
//
NSString *const kQiniuBucketName = @"iosaudio";
// 访
NSString *const kQiniuDomain = @"iosaudio.daoqi88.cn";
//
NSString *const kQiniuRecordingDirectory = @"";

View File

@@ -0,0 +1,76 @@
//
// QiniuManager.h
// msext
//
// Created on June 15, 2025.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
* 上传完成后的回调
* @param key 文件在七牛云中的key
* @param error 错误信息如果上传成功则为nil
*/
typedef void(^QiniuUploadCompletionHandler)(NSString * _Nullable key, NSError * _Nullable error);
/**
* 下载完成后的回调
* @param filePath 下载文件的本地路径
* @param error 错误信息如果下载成功则为nil
*/
typedef void(^QiniuDownloadCompletionHandler)(NSString * _Nullable filePath, NSError * _Nullable error);
/**
* 进度回调
* @param progress 进度值范围0-1
*/
typedef void(^QiniuProgressHandler)(float progress);
@interface QiniuManager : NSObject
/**
* 获取QiniuManager的单例
* @return QiniuManager实例
*/
+ (instancetype)sharedManager;
/**
* 初始化七牛云SDK
*/
- (void)setupQiniuSDK;
/**
* 上传本地音频文件到七牛云
* @param filePath 本地文件路径
* @param fileName 上传后的文件名(不含路径)
* @param progressHandler 上传进度回调
* @param completionHandler 上传完成回调
*/
- (void)uploadAudioFile:(NSString *)filePath
fileName:(NSString *)fileName
progressHandler:(nullable QiniuProgressHandler)progressHandler
completionHandler:(QiniuUploadCompletionHandler)completionHandler;
/**
* 从七牛云下载音频文件
* @param key 文件在七牛云中的key
* @param progressHandler 下载进度回调
* @param completionHandler 下载完成回调
*/
- (void)downloadAudioFile:(NSString *)key
progressHandler:(nullable QiniuProgressHandler)progressHandler
completionHandler:(QiniuDownloadCompletionHandler)completionHandler;
/**
* 根据key获取七牛云文件的URL
* @param key 文件在七牛云中的key
* @return 完整的URL字符串
*/
- (NSString *)getFileUrlWithKey:(NSString *)key;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,256 @@
//
// QiniuManager.m
// msext
//
// Created on June 15, 2025.
//
#import "QiniuManager.h"
#import <QiniuSDK.h>
#import "QiniuConfig.h"
#import <objc/runtime.h>
#import <CommonCrypto/CommonHMAC.h>
#import <CommonCrypto/CommonDigest.h>
@implementation QiniuManager
+ (instancetype)sharedManager {
static QiniuManager *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
[instance setupQiniuSDK];
});
return instance;
}
- (void)setupQiniuSDK {
// Qiniu SDK doesn't require explicit initialization
// The configuration will be applied when creating the QNUploadManager instance
}
- (void)uploadAudioFile:(NSString *)filePath
fileName:(NSString *)fileName
progressHandler:(QiniuProgressHandler)progressHandler
completionHandler:(QiniuUploadCompletionHandler)completionHandler {
//
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
NSError *error = [NSError errorWithDomain:@"QiniuManager" code:-1 userInfo:@{NSLocalizedDescriptionKey: @"上传文件不存在"}];
if (completionHandler) {
completionHandler(nil, error);
}
return;
}
//
QNConfiguration *config = [QNConfiguration build:^(QNConfigurationBuilder *builder) {
builder.zone = [QNFixedZone zone2]; // 使
builder.timeoutInterval = 60; //
}];
//
QNUploadManager *uploadManager = [[QNUploadManager alloc] initWithConfiguration:config];
NSString *token = [self generateUploadToken];
//
NSString *key = [NSString stringWithFormat:@"%@%@", kQiniuRecordingDirectory, fileName];
//
QNUploadOption *option = [[QNUploadOption alloc] initWithMime:@"audio/amr"
progressHandler:^(NSString *key, float percent) {
if (progressHandler) {
progressHandler(percent);
}
} params:nil checkCrc:NO cancellationSignal:nil];
//
[uploadManager putFile:filePath
key:key
token:token
complete:^(QNResponseInfo *info, NSString *key, NSDictionary *resp) {
if (info.statusCode == 200) {
if (completionHandler) {
completionHandler(key, nil);
}
} else {
NSError *error = [NSError errorWithDomain:@"QiniuManager"
code:info.statusCode
userInfo:@{NSLocalizedDescriptionKey: info.error.localizedDescription ?: @"上传失败"}];
if (completionHandler) {
completionHandler(nil, error);
}
}
}
option:option];
}
- (void)downloadAudioFile:(NSString *)key
progressHandler:(QiniuProgressHandler)progressHandler
completionHandler:(QiniuDownloadCompletionHandler)completionHandler {
// URL
NSString *urlString = [self getFileUrlWithKey:key];
NSURL *url = [NSURL URLWithString:urlString];
//
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
if (error) {
if (completionHandler) {
completionHandler(nil, error);
}
return;
}
// URL
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if (httpResponse.statusCode != 200) {
NSError *downloadError = [NSError errorWithDomain:@"QiniuManager"
code:httpResponse.statusCode
userInfo:@{NSLocalizedDescriptionKey: @"下载失败"}];
if (completionHandler) {
completionHandler(nil, downloadError);
}
return;
}
// URL
NSString *fileName = [key lastPathComponent];
if ([fileName length] == 0) {
fileName = [NSString stringWithFormat:@"qiniu_download_%@", [[NSUUID UUID] UUIDString]];
}
//
NSString *cachesDirectory = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
NSString *downloadDirectory = [cachesDirectory stringByAppendingPathComponent:@"QiniuDownloads"];
//
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:downloadDirectory]) {
[fileManager createDirectoryAtPath:downloadDirectory withIntermediateDirectories:YES attributes:nil error:nil];
}
//
NSString *destinationPath = [downloadDirectory stringByAppendingPathComponent:fileName];
//
if ([fileManager fileExistsAtPath:destinationPath]) {
[fileManager removeItemAtPath:destinationPath error:nil];
}
NSError *moveError = nil;
[fileManager moveItemAtURL:location toURL:[NSURL fileURLWithPath:destinationPath] error:&moveError];
if (moveError) {
if (completionHandler) {
completionHandler(nil, moveError);
}
} else {
if (completionHandler) {
completionHandler(destinationPath, nil);
}
}
}];
//
if (progressHandler) {
[downloadTask addObserver:self forKeyPath:@"countOfBytesReceived" options:NSKeyValueObservingOptionNew context:NULL];
objc_setAssociatedObject(downloadTask, "progressHandler", [progressHandler copy], OBJC_ASSOCIATION_COPY);
objc_setAssociatedObject(downloadTask, "totalBytes", @(0), OBJC_ASSOCIATION_RETAIN);
}
//
[downloadTask resume];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([object isKindOfClass:[NSURLSessionDownloadTask class]]) {
NSURLSessionDownloadTask *task = (NSURLSessionDownloadTask *)object;
if ([keyPath isEqualToString:@"countOfBytesReceived"]) {
NSNumber *totalBytes = objc_getAssociatedObject(task, "totalBytes");
if ([totalBytes longLongValue] == 0 && task.countOfBytesExpectedToReceive > 0) {
objc_setAssociatedObject(task, "totalBytes", @(task.countOfBytesExpectedToReceive), OBJC_ASSOCIATION_RETAIN);
}
float progress = 0;
if (task.countOfBytesExpectedToReceive > 0) {
progress = (float)task.countOfBytesReceived / (float)task.countOfBytesExpectedToReceive;
}
QiniuProgressHandler progressHandler = objc_getAssociatedObject(task, "progressHandler");
if (progressHandler) {
dispatch_async(dispatch_get_main_queue(), ^{
progressHandler(progress);
});
}
}
}
}
- (NSString *)getFileUrlWithKey:(NSString *)key {
return [NSString stringWithFormat:@"%@/%@", kQiniuDomain, key];
}
#pragma mark - Private Methods
- (NSString *)generateUploadToken {
// (putPolicy)
NSMutableDictionary *policy = [NSMutableDictionary dictionary];
// bucketNamebucketName:keyPrefix
NSString *scope = [NSString stringWithFormat:@"%@", kQiniuBucketName];
[policy setObject:scope forKey:@"scope"];
// 1
NSInteger deadline = (NSInteger)[[NSDate dateWithTimeIntervalSinceNow:3600] timeIntervalSince1970];
[policy setObject:@(deadline) forKey:@"deadline"];
// JSON
NSError *error = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:policy options:0 error:&error];
if (error) {
NSLog(@"生成JSON数据失败: %@", error);
return nil;
}
// Base64JSON
NSString *encodedPolicy = [self urlsafeBase64EncodeData:jsonData];
// 使HMAC-SHA1encodedPolicy
NSData *signData = [encodedPolicy dataUsingEncoding:NSUTF8StringEncoding];
NSString *encodedSign = [self hmacSha1:kQiniuSecretKey data:signData];
// - : accessKey:encodedSign:encodedPolicy
NSString *uploadToken = [NSString stringWithFormat:@"%@:%@:%@", kQiniuAccessKey, encodedSign, encodedPolicy];
// NSLog(@"七牛云配置信息 - AccessKey: %@, BucketName: %@, Domain: %@",
// kQiniuAccessKey, kQiniuBucketName, kQiniuDomain);
// NSLog(@"生成的上传凭证: %@", uploadToken);
return uploadToken;
}
#pragma mark - Utility Methods
- (NSString *)urlsafeBase64EncodeData:(NSData *)data {
NSString *base64 = [data base64EncodedStringWithOptions:0];
base64 = [base64 stringByReplacingOccurrencesOfString:@"+" withString:@"-"];
base64 = [base64 stringByReplacingOccurrencesOfString:@"/" withString:@"_"];
return base64;
}
- (NSString *)hmacSha1:(NSString *)key data:(NSData *)data {
const char *cKey = [key cStringUsingEncoding:NSUTF8StringEncoding];
const char *cData = [data bytes];
unsigned char cHMAC[CC_SHA1_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), cData, [data length], cHMAC);
NSData *hmacData = [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
return [self urlsafeBase64EncodeData:hmacData];
}
@end

View File

@@ -1,28 +0,0 @@
//
// QNALAssetFile.h
// QiniuSDK
//
// Created by bailong on 15/7/25.
// Copyright (c) 2015年 Qiniu. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "QNFileDelegate.h"
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED)
@class ALAsset;
@interface QNALAssetFile : NSObject <QNFileDelegate>
/**
* 打开指定文件
*
* @param path 文件路径
* @param error 输出的错误信息
*
* @return 实例
*/
- (instancetype)init:(ALAsset *)asset
error:(NSError *__autoreleasing *)error;
@end
#endif

View File

@@ -1,72 +0,0 @@
//
// QNALAssetFile.m
// QiniuSDK
//
// Created by bailong on 15/7/25.
// Copyright (c) 2015 Qiniu. All rights reserved.
//
#import "QNALAssetFile.h"
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
#import <AssetsLibrary/AssetsLibrary.h>
#import "QNResponseInfo.h"
@interface QNALAssetFile ()
@property (nonatomic) ALAsset *asset;
@property (readonly) int64_t fileSize;
@property (readonly) int64_t fileModifyTime;
@end
@implementation QNALAssetFile
- (instancetype)init:(ALAsset *)asset
error:(NSError *__autoreleasing *)error {
if (self = [super init]) {
NSDate *createTime = [asset valueForProperty:ALAssetPropertyDate];
int64_t t = 0;
if (createTime != nil) {
t = [createTime timeIntervalSince1970];
}
_fileModifyTime = t;
_fileSize = asset.defaultRepresentation.size;
_asset = asset;
}
return self;
}
- (NSData *)read:(long)offset
size:(long)size {
ALAssetRepresentation *rep = [self.asset defaultRepresentation];
Byte *buffer = (Byte *)malloc(size);
NSUInteger buffered = [rep getBytes:buffer fromOffset:offset length:size error:nil];
return [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];
}
- (NSData *)readAll {
return [self read:0 size:(long)_fileSize];
}
- (void)close {
}
- (NSString *)path {
ALAssetRepresentation *rep = [self.asset defaultRepresentation];
return [rep url].path;
}
- (int64_t)modifyTime {
return _fileModifyTime;
}
- (int64_t)size {
return _fileSize;
}
@end
#endif

View File

@@ -1,13 +0,0 @@
//
// QNAsyncRun.h
// QiniuSDK
//
// Created by bailong on 14/10/17.
// Copyright (c) 2014年 Qiniu. All rights reserved.
//
typedef void (^QNRun)(void);
void QNAsyncRun(QNRun run);
void QNAsyncRunInMain(QNRun run);

View File

@@ -1,22 +0,0 @@
//
// QNAsyncRun.m
// QiniuSDK
//
// Created by bailong on 14/10/17.
// Copyright (c) 2014 Qiniu. All rights reserved.
//
#import "QNAsyncRun.h"
#import <Foundation/Foundation.h>
void QNAsyncRun(QNRun run) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
run();
});
}
void QNAsyncRunInMain(QNRun run) {
dispatch_async(dispatch_get_main_queue(), ^(void) {
run();
});
}

View File

@@ -1,36 +0,0 @@
//
// QNCrc.h
// QiniuSDK
//
// Created by bailong on 14-9-29.
// Copyright (c) 2014年 Qiniu. All rights reserved.
//
#import <Foundation/Foundation.h>
/**
* 生成crc32 校验码
*/
@interface QNCrc32 : NSObject
/**
* 文件校验
*
* @param filePath 文件路径
* @param error 文件读取错误
*
* @return 校验码
*/
+ (UInt32)file:(NSString *)filePath
error:(NSError **)error;
/**
* 二进制字节校验
*
* @param data 二进制数据
*
* @return 校验码
*/
+ (UInt32)data:(NSData *)data;
@end

View File

@@ -1,45 +0,0 @@
//
// QNCrc.m
// QiniuSDK
//
// Created by bailong on 14-9-29.
// Copyright (c) 2014 Qiniu. All rights reserved.
//
#import <zlib.h>
#import "QNConfiguration.h"
#import "QNCrc32.h"
@implementation QNCrc32
+ (UInt32)data:(NSData *)data {
uLong crc = crc32(0L, Z_NULL, 0);
crc = crc32(crc, [data bytes], (uInt)[data length]);
return (UInt32)crc;
}
+ (UInt32)file:(NSString *)filePath
error:(NSError **)error {
@autoreleasepool {
NSData *data = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:error];
if (*error != nil) {
return 0;
}
int len = (int)[data length];
int count = (len + kQNBlockSize - 1) / kQNBlockSize;
uLong crc = crc32(0L, Z_NULL, 0);
for (int i = 0; i < count; i++) {
int offset = i * kQNBlockSize;
int size = (len - offset) > kQNBlockSize ? kQNBlockSize : (len - offset);
NSData *d = [data subdataWithRange:NSMakeRange(offset, (unsigned int)size)];
crc = crc32(crc, [d bytes], (uInt)[d length]);
}
return (UInt32)crc;
}
}
@end

View File

@@ -1,35 +0,0 @@
//
// QNEtag.h
// QiniuSDK
//
// Created by bailong on 14/10/4.
// Copyright (c) 2014年 Qiniu. All rights reserved.
//
#import <Foundation/Foundation.h>
/**
* 服务器 hash etag 生成
*/
@interface QNEtag : NSObject
/**
* 文件etag
*
* @param filePath 文件路径
* @param error 输出文件读取错误
*
* @return etag
*/
+ (NSString *)file:(NSString *)filePath
error:(NSError **)error;
/**
* 二进制数据etag
*
* @param data 数据
*
* @return etag
*/
+ (NSString *)data:(NSData *)data;
@end

View File

@@ -1,60 +0,0 @@
//
// QNEtag.m
// QiniuSDK
//
// Created by bailong on 14/10/4.
// Copyright (c) 2014 Qiniu. All rights reserved.
//
#include <CommonCrypto/CommonCrypto.h>
#import "QNConfiguration.h"
#import "QNEtag.h"
#import "QNUrlSafeBase64.h"
@implementation QNEtag
+ (NSString *)file:(NSString *)filePath
error:(NSError **)error {
@autoreleasepool {
NSData *data = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:error];
if (*error != nil) {
return 0;
}
return [QNEtag data:data];
}
}
+ (NSString *)data:(NSData *)data {
if (data == nil || [data length] == 0) {
return @"Fto5o-5ea0sNMlW_75VgGJCv2AcJ";
}
int len = (int)[data length];
int count = (len + kQNBlockSize - 1) / kQNBlockSize;
NSMutableData *retData = [NSMutableData dataWithLength:CC_SHA1_DIGEST_LENGTH + 1];
UInt8 *ret = [retData mutableBytes];
NSMutableData *blocksSha1 = nil;
UInt8 *pblocksSha1 = ret + 1;
if (count > 1) {
blocksSha1 = [NSMutableData dataWithLength:CC_SHA1_DIGEST_LENGTH * count];
pblocksSha1 = [blocksSha1 mutableBytes];
}
for (int i = 0; i < count; i++) {
int offset = i * kQNBlockSize;
int size = (len - offset) > kQNBlockSize ? kQNBlockSize : (len - offset);
NSData *d = [data subdataWithRange:NSMakeRange(offset, (unsigned int)size)];
CC_SHA1([d bytes], (CC_LONG)size, pblocksSha1 + i * CC_SHA1_DIGEST_LENGTH);
}
if (count == 1) {
ret[0] = 0x16;
} else {
ret[0] = 0x96;
CC_SHA1(pblocksSha1, (CC_LONG)CC_SHA1_DIGEST_LENGTH * count, ret + 1);
}
return [QNUrlSafeBase64 encodeData:retData];
}
@end

View File

@@ -1,24 +0,0 @@
//
// QNFile.h
// QiniuSDK
//
// Created by bailong on 15/7/25.
// Copyright (c) 2015年 Qiniu. All rights reserved.
//
#import "QNFileDelegate.h"
#import <Foundation/Foundation.h>
@interface QNFile : NSObject <QNFileDelegate>
/**
* 打开指定文件
*
* @param path 文件路径
* @param error 输出的错误信息
*
* @return 实例
*/
- (instancetype)init:(NSString *)path
error:(NSError *__autoreleasing *)error;
@end

View File

@@ -1,106 +0,0 @@
//
// QNFile.m
// QiniuSDK
//
// Created by bailong on 15/7/25.
// Copyright (c) 2015 Qiniu. All rights reserved.
//
#import "QNFile.h"
#import "QNResponseInfo.h"
@interface QNFile ()
@property (nonatomic, readonly) NSString *filepath;
@property (nonatomic) NSData *data;
@property (readonly) int64_t fileSize;
@property (readonly) int64_t fileModifyTime;
@property (nonatomic) NSFileHandle *file;
@end
@implementation QNFile
- (instancetype)init:(NSString *)path
error:(NSError *__autoreleasing *)error {
if (self = [super init]) {
_filepath = path;
NSError *error2 = nil;
NSDictionary *fileAttr = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:&error2];
if (error2 != nil) {
if (error != nil) {
*error = error2;
}
return self;
}
_fileSize = [fileAttr fileSize];
NSDate *modifyTime = fileAttr[NSFileModificationDate];
int64_t t = 0;
if (modifyTime != nil) {
t = [modifyTime timeIntervalSince1970];
}
_fileModifyTime = t;
NSFileHandle *f = nil;
NSData *d = nil;
//[NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:&error] 200Mfilehandle
// https://issues.apache.org/jira/browse/CB-5790
if (_fileSize > 16 * 1024 * 1024) {
f = [NSFileHandle fileHandleForReadingAtPath:path];
if (f == nil) {
if (error != nil) {
*error = [[NSError alloc] initWithDomain:path code:kQNFileError userInfo:nil];
}
return self;
}
} else {
d = [NSData dataWithContentsOfFile:path options:NSDataReadingMappedIfSafe error:&error2];
if (error2 != nil) {
if (error != nil) {
*error = error2;
}
return self;
}
}
_file = f;
_data = d;
}
return self;
}
- (NSData *)read:(long)offset
size:(long)size {
if (_data != nil) {
return [_data subdataWithRange:NSMakeRange(offset, (unsigned int)size)];
}
[_file seekToFileOffset:offset];
return [_file readDataOfLength:size];
}
- (NSData *)readAll {
return [self read:0 size:(long)_fileSize];
}
- (void)close {
if (_file != nil) {
[_file closeFile];
}
}
- (NSString *)path {
return _filepath;
}
- (int64_t)modifyTime {
return _fileModifyTime;
}
- (int64_t)size {
return _fileSize;
}
@end

View File

@@ -1,61 +0,0 @@
//
// QNFileDelegate.h
// QiniuSDK
//
// Created by bailong on 15/7/25.
// Copyright (c) 2015年 Qiniu. All rights reserved.
//
#import <Foundation/Foundation.h>
/**
* 文件处理接口支持ALAsset, NSFileHandle, NSData
*/
@protocol QNFileDelegate <NSObject>
/**
* 从指定偏移读取数据
*
* @param offset 偏移地址
* @param size 大小
*
* @return 数据
*/
- (NSData *)read:(long)offset
size:(long)size;
/**
* 读取所有文件内容
*
* @return 数据
*/
- (NSData *)readAll;
/**
* 关闭文件
*
*/
- (void)close;
/**
* 文件路径
*
* @return 文件路径
*/
- (NSString *)path;
/**
* 文件修改时间
*
* @return 修改时间
*/
- (int64_t)modifyTime;
/**
* 文件大小
*
* @return 文件大小
*/
- (int64_t)size;
@end

View File

@@ -1,27 +0,0 @@
//
// QNPHAssetFile.h
// Pods
//
// Created by 何舒 on 15/10/21.
//
//
#import <Foundation/Foundation.h>
#import "QNFileDelegate.h"
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000)
@class PHAsset;
@interface QNPHAssetFile : NSObject <QNFileDelegate>
/**
* 打开指定文件
*
* @param path 文件路径
* @param error 输出的错误信息
*
* @return 实例
*/
- (instancetype)init:(PHAsset *)phAsset
error:(NSError *__autoreleasing *)error;
@end
#endif

View File

@@ -1,165 +0,0 @@
//
// QNPHAssetFile.m
// Pods
//
// Created by on 15/10/21.
//
//
#import "QNPHAssetFile.h"
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000)
#import <AVFoundation/AVFoundation.h>
#import <Photos/Photos.h>
#import "QNResponseInfo.h"
@interface QNPHAssetFile ()
@property (nonatomic) PHAsset *phAsset;
@property (readonly) int64_t fileSize;
@property (readonly) int64_t fileModifyTime;
@property (nonatomic, strong) NSData *assetData;
@property (nonatomic, strong) NSURL *assetURL;
@property (nonatomic, readonly) NSString *filepath;
@property (nonatomic) NSFileHandle *file;
@end
@implementation QNPHAssetFile
- (instancetype)init:(PHAsset *)phAsset error:(NSError *__autoreleasing *)error {
if (self = [super init]) {
NSDate *createTime = phAsset.creationDate;
int64_t t = 0;
if (createTime != nil) {
t = [createTime timeIntervalSince1970];
}
_fileModifyTime = t;
_phAsset = phAsset;
_filepath = [self getInfo];
if (PHAssetMediaTypeVideo == self.phAsset.mediaType) {
NSError *error2 = nil;
NSDictionary *fileAttr = [[NSFileManager defaultManager] attributesOfItemAtPath:_filepath error:&error2];
if (error2 != nil) {
if (error != nil) {
*error = error2;
}
return self;
}
_fileSize = [fileAttr fileSize];
NSFileHandle *f = nil;
NSData *d = nil;
if (_fileSize > 16 * 1024 * 1024) {
f = [NSFileHandle fileHandleForReadingAtPath:_filepath];
if (f == nil) {
if (error != nil) {
*error = [[NSError alloc] initWithDomain:_filepath code:kQNFileError userInfo:nil];
}
return self;
}
} else {
d = [NSData dataWithContentsOfFile:_filepath options:NSDataReadingMappedIfSafe error:&error2];
if (error2 != nil) {
if (error != nil) {
*error = error2;
}
return self;
}
}
_file = f;
_assetData = d;
}
}
return self;
}
- (NSData *)read:(long)offset size:(long)size {
if (_assetData != nil) {
return [_assetData subdataWithRange:NSMakeRange(offset, (unsigned int)size)];
}
[_file seekToFileOffset:offset];
return [_file readDataOfLength:size];
}
- (NSData *)readAll {
return [self read:0 size:(long)_fileSize];
}
- (void)close {
if (PHAssetMediaTypeVideo == self.phAsset.mediaType) {
if (_file != nil) {
[_file closeFile];
}
[[NSFileManager defaultManager] removeItemAtPath:_filepath error:nil];
}
}
- (NSString *)path {
return _filepath;
}
- (int64_t)modifyTime {
return _fileModifyTime;
}
- (int64_t)size {
return _fileSize;
}
- (NSString *)getInfo {
__block NSString *filePath = nil;
if (PHAssetMediaTypeImage == self.phAsset.mediaType) {
PHImageRequestOptions *options = [PHImageRequestOptions new];
options.version = PHImageRequestOptionsVersionCurrent;
options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
options.resizeMode = PHImageRequestOptionsResizeModeNone;
//icloud
options.networkAccessAllowed = NO;
options.synchronous = YES;
[[PHImageManager defaultManager] requestImageDataForAsset:self.phAsset
options:options
resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
_assetData = imageData;
_fileSize = imageData.length;
_assetURL = [NSURL URLWithString:self.phAsset.localIdentifier];
filePath = _assetURL.path;
}];
} else if (PHAssetMediaTypeVideo == self.phAsset.mediaType) {
NSArray *assetResources = [PHAssetResource assetResourcesForAsset:self.phAsset];
PHAssetResource *resource;
for (PHAssetResource *assetRes in assetResources) {
if (assetRes.type == PHAssetResourceTypePairedVideo || assetRes.type == PHAssetResourceTypeVideo) {
resource = assetRes;
}
}
NSString *fileName = @"tempAssetVideo.mov";
if (resource.originalFilename) {
fileName = resource.originalFilename;
}
PHAssetResourceRequestOptions *options = [PHAssetResourceRequestOptions new];
//icloud
options.networkAccessAllowed = NO;
NSString *PATH_VIDEO_FILE = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
[[NSFileManager defaultManager] removeItemAtPath:PATH_VIDEO_FILE error:nil];
[[PHAssetResourceManager defaultManager] writeDataForAssetResource:resource toFile:[NSURL fileURLWithPath:PATH_VIDEO_FILE] options:options completionHandler:^(NSError *_Nullable error) {
if (error) {
filePath = nil;
} else {
filePath = PATH_VIDEO_FILE;
}
}];
}
return filePath;
}
@end
#endif

View File

@@ -1,31 +0,0 @@
//
// QNPHAssetResource.h
// QiniuSDK
//
// Created by 何舒 on 16/2/14.
// Copyright © 2016年 Qiniu. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "QNFileDelegate.h"
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 90100)
@class PHAssetResource;
@interface QNPHAssetResource : NSObject <QNFileDelegate>
/**
* 打开指定文件
*
* @param path PHLivePhoto的PHAssetResource文件
* @param error 输出的错误信息
*
* @return 实例
*/
- (instancetype)init:(PHAssetResource *)phAssetResource
error:(NSError *__autoreleasing *)error;
@end
#endif

View File

@@ -1,175 +0,0 @@
//
// QNPHAssetResource.m
// QiniuSDK
//
// Created by on 16/2/14.
// Copyright © 2016 Qiniu. All rights reserved.
//
#import "QNPHAssetResource.h"
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 90100)
#import <AVFoundation/AVFoundation.h>
#import <Photos/Photos.h>
enum {
kAMASSETMETADATA_PENDINGREADS = 1,
kAMASSETMETADATA_ALLFINISHED = 0
};
#import "QNResponseInfo.h"
@interface QNPHAssetResource ()
{
BOOL _hasGotInfo;
}
@property (nonatomic) PHAsset *phAsset;
@property (nonatomic) PHLivePhoto *phLivePhoto;
@property (nonatomic) PHAssetResource *phAssetResource;
@property (readonly) int64_t fileSize;
@property (readonly) int64_t fileModifyTime;
@property (nonatomic, strong) NSData *assetData;
@property (nonatomic, strong) NSURL *assetURL;
@end
@implementation QNPHAssetResource
- (instancetype)init:(PHAssetResource *)phAssetResource
error:(NSError *__autoreleasing *)error {
if (self = [super init]) {
PHAsset *phasset = [PHAsset fetchAssetsWithBurstIdentifier:self.phAssetResource.assetLocalIdentifier options:nil][0];
NSDate *createTime = phasset.creationDate;
int64_t t = 0;
if (createTime != nil) {
t = [createTime timeIntervalSince1970];
}
_fileModifyTime = t;
_phAssetResource = phAssetResource;
[self getInfo];
}
return self;
}
- (NSData *)read:(long)offset size:(long)size {
NSRange subRange = NSMakeRange(offset, size);
if (!self.assetData) {
self.assetData = [self fetchDataFromAsset:self.phAssetResource];
}
NSData *subData = [self.assetData subdataWithRange:subRange];
return subData;
}
- (NSData *)readAll {
return [self read:0 size:(long)_fileSize];
}
- (void)close {
}
- (NSString *)path {
return self.assetURL.path;
}
- (int64_t)modifyTime {
return _fileModifyTime;
}
- (int64_t)size {
return _fileSize;
}
- (void)getInfo {
if (!_hasGotInfo) {
_hasGotInfo = YES;
NSConditionLock *assetReadLock = [[NSConditionLock alloc] initWithCondition:kAMASSETMETADATA_PENDINGREADS];
NSString *pathToWrite = [NSTemporaryDirectory() stringByAppendingString:self.phAssetResource.originalFilename];
NSURL *localpath = [NSURL fileURLWithPath:pathToWrite];
PHAssetResourceRequestOptions *options = [PHAssetResourceRequestOptions new];
options.networkAccessAllowed = YES;
[[PHAssetResourceManager defaultManager] writeDataForAssetResource:self.phAssetResource toFile:localpath options:options completionHandler:^(NSError *_Nullable error) {
if (error == nil) {
AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:localpath options:nil];
NSNumber *fileSize = nil;
[urlAsset.URL getResourceValue:&fileSize forKey:NSURLFileSizeKey error:nil];
_fileSize = [fileSize unsignedLongLongValue];
_assetURL = urlAsset.URL;
self.assetData = [NSData dataWithData:[NSData dataWithContentsOfURL:urlAsset.URL]];
} else {
NSLog(@"%@", error);
}
BOOL blHave = [[NSFileManager defaultManager] fileExistsAtPath:pathToWrite];
if (!blHave) {
NSLog(@"no have");
return;
} else {
NSLog(@" have");
BOOL blDele = [[NSFileManager defaultManager] removeItemAtPath:pathToWrite error:nil];
if (blDele) {
NSLog(@"dele success");
} else {
NSLog(@"dele fail");
}
}
[assetReadLock lock];
[assetReadLock unlockWithCondition:kAMASSETMETADATA_ALLFINISHED];
}];
[assetReadLock lockWhenCondition:kAMASSETMETADATA_ALLFINISHED];
[assetReadLock unlock];
assetReadLock = nil;
}
}
- (NSData *)fetchDataFromAsset:(PHAssetResource *)videoResource {
__block NSData *tmpData = [NSData data];
NSConditionLock *assetReadLock = [[NSConditionLock alloc] initWithCondition:kAMASSETMETADATA_PENDINGREADS];
NSString *pathToWrite = [NSTemporaryDirectory() stringByAppendingString:videoResource.originalFilename];
NSURL *localpath = [NSURL fileURLWithPath:pathToWrite];
PHAssetResourceRequestOptions *options = [PHAssetResourceRequestOptions new];
options.networkAccessAllowed = YES;
[[PHAssetResourceManager defaultManager] writeDataForAssetResource:videoResource toFile:localpath options:options completionHandler:^(NSError *_Nullable error) {
if (error == nil) {
AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:localpath options:nil];
NSData *videoData = [NSData dataWithContentsOfURL:urlAsset.URL];
tmpData = [NSData dataWithData:videoData];
} else {
NSLog(@"%@", error);
}
BOOL blHave = [[NSFileManager defaultManager] fileExistsAtPath:pathToWrite];
if (!blHave) {
NSLog(@"no have");
return;
} else {
NSLog(@" have");
BOOL blDele = [[NSFileManager defaultManager] removeItemAtPath:pathToWrite error:nil];
if (blDele) {
NSLog(@"dele success");
} else {
NSLog(@"dele fail");
}
}
[assetReadLock lock];
[assetReadLock unlockWithCondition:kAMASSETMETADATA_ALLFINISHED];
}];
[assetReadLock lockWhenCondition:kAMASSETMETADATA_ALLFINISHED];
[assetReadLock unlock];
assetReadLock = nil;
return tmpData;
}
@end
#endif

View File

@@ -1,20 +0,0 @@
//
// QNSystem.h
// QiniuSDK
//
// Created by bailong on 15/10/13.
// Copyright © 2015年 Qiniu. All rights reserved.
//
#ifndef QNSystem_h
#define QNSystem_h
BOOL hasNSURLSession();
BOOL hasAts();
BOOL allowsArbitraryLoads();
BOOL isIpV6FullySupported();
#endif /* QNSystem_h */

View File

@@ -1,89 +0,0 @@
//
// QNSystem.m
// QiniuSDK
//
// Created by bailong on 15/10/13.
// Copyright © 2015 Qiniu. All rights reserved.
//
#import <Foundation/Foundation.h>
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#import <MobileCoreServices/MobileCoreServices.h>
#import <UIKit/UIKit.h>
#else
#import <CoreServices/CoreServices.h>
#endif
BOOL hasNSURLSession() {
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED)
float sysVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
if (sysVersion < 7.0) {
return NO;
}
#else
NSOperatingSystemVersion sysVersion = [[NSProcessInfo processInfo] operatingSystemVersion];
if (sysVersion.majorVersion < 10) {
return NO;
} else if (sysVersion.majorVersion == 10) {
return sysVersion.minorVersion >= 9;
}
#endif
return YES;
}
BOOL hasAts() {
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED)
float sysVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
if (sysVersion < 9.0) {
return NO;
}
#else
NSOperatingSystemVersion sysVersion = [[NSProcessInfo processInfo] operatingSystemVersion];
if (sysVersion.majorVersion < 10) {
return NO;
} else if (sysVersion.majorVersion == 10) {
return sysVersion.minorVersion >= 11;
}
#endif
return YES;
}
BOOL allowsArbitraryLoads() {
if (!hasAts()) {
return YES;
}
// for unit test
NSDictionary* d = [[NSBundle mainBundle] infoDictionary];
if (d == nil || d.count == 0) {
return YES;
}
NSDictionary* sec = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSAppTransportSecurity"];
if (sec == nil) {
return NO;
}
NSNumber* ats = [sec objectForKey:@"NSAllowsArbitraryLoads"];
if (ats == nil) {
return NO;
}
return ats.boolValue;
}
BOOL isIpV6FullySupported() {
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED)
float sysVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
if (sysVersion < 9.0) {
return NO;
}
#else
NSOperatingSystemVersion sysVersion = [[NSProcessInfo processInfo] operatingSystemVersion];
if (sysVersion.majorVersion < 10) {
return NO;
} else if (sysVersion.majorVersion == 10) {
return sysVersion.minorVersion >= 11;
}
#endif
return YES;
}

View File

@@ -1,41 +0,0 @@
//
// QiniuSDK
//
// Created by bailong on 14-9-28.
// Copyright (c) 2014年 Qiniu. All rights reserved.
//
#import <Foundation/Foundation.h>
/**
* url safe base64 编码类, 对/ 做了处理
*/
@interface QNUrlSafeBase64 : NSObject
/**
* 字符串编码
*
* @param source 字符串
*
* @return base64 字符串
*/
+ (NSString *)encodeString:(NSString *)source;
/**
* 二进制数据编码
*
* @param source 二进制数据
*
* @return base64字符串
*/
+ (NSString *)encodeData:(NSData *)source;
/**
* 字符串解码
*
* @param base64 字符串
*
* @return 数据
*/
+ (NSData *)decodeString:(NSString *)data;
@end

View File

@@ -1,29 +0,0 @@
//
// QiniuSDK
//
// Created by bailong on 14-9-28.
// Copyright (c) 2014 Qiniu. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "QNUrlSafeBase64.h"
#import "QN_GTM_Base64.h"
@implementation QNUrlSafeBase64
+ (NSString *)encodeString:(NSString *)sourceString {
NSData *data = [NSData dataWithBytes:[sourceString UTF8String] length:[sourceString lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];
return [self encodeData:data];
}
+ (NSString *)encodeData:(NSData *)data {
return [QN_GTM_Base64 stringByWebSafeEncodingData:data padded:YES];
}
+ (NSData *)decodeString:(NSString *)data {
return [QN_GTM_Base64 webSafeDecodeString:data];
}
@end

View File

@@ -1,14 +0,0 @@
//
// QNVersion.h
// QiniuSDK
//
// Created by bailong on 14-9-29.
// Copyright (c) 2014年 Qiniu. All rights reserved.
//
#import <Foundation/Foundation.h>
/**
* sdk 版本
*/
static const NSString *kQiniuVersion = @"7.1.5";

View File

@@ -1,182 +0,0 @@
//
// GTMBase64.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>
// GTMBase64
//
/// 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 QN_GTM_Base64 : 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

View File

@@ -1,693 +0,0 @@
//
// GTMBase64.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 "QN_GTM_Base64.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.
//
BOOL QN_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
//
NSUInteger QN_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
//
NSUInteger QN_GuessDecodedLength(NSUInteger srcLen) {
return (srcLen + 3) / 4 * 3;
}
@interface QN_GTM_Base64 (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 QN_GTM_Base64
//
// 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 QN_GTM_Base64 (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 = QN_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 = QN_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 (QN_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 (!QN_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 (!QN_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

View File

@@ -1,33 +0,0 @@
#import <Foundation/Foundation.h>
@class QNResponseInfo;
typedef void (^QNInternalProgressBlock)(long long totalBytesWritten, long long totalBytesExpectedToWrite);
typedef void (^QNCompleteBlock)(QNResponseInfo *info, NSDictionary *resp);
typedef BOOL (^QNCancelBlock)(void);
/**
* Http 客户端接口
*/
@protocol QNHttpDelegate <NSObject>
- (void)multipartPost:(NSString *)url
withData:(NSData *)data
withParams:(NSDictionary *)params
withFileName:(NSString *)key
withMimeType:(NSString *)mime
withCompleteBlock:(QNCompleteBlock)completeBlock
withProgressBlock:(QNInternalProgressBlock)progressBlock
withCancelBlock:(QNCancelBlock)cancelBlock
withAccess:(NSString *)access;
- (void)post:(NSString *)url
withData:(NSData *)data
withParams:(NSDictionary *)params
withHeaders:(NSDictionary *)headers
withCompleteBlock:(QNCompleteBlock)completeBlock
withProgressBlock:(QNInternalProgressBlock)progressBlock
withCancelBlock:(QNCancelBlock)cancelBlock
withAccess:(NSString *)access;
@end

View File

@@ -1,206 +0,0 @@
//
// QNResponseInfo.h
// QiniuSDK
//
// Created by bailong on 14/10/2.
// Copyright (c) 2014年 Qiniu. All rights reserved.
//
#import <Foundation/Foundation.h>
/**
* 中途取消的状态码
*/
extern const int kQNRequestCancelled;
/**
* 网络错误状态码
*/
extern const int kQNNetworkError;
/**
* 错误参数状态码
*/
extern const int kQNInvalidArgument;
/**
* 0 字节文件或数据
*/
extern const int kQNZeroDataSize;
/**
* 错误token状态码
*/
extern const int kQNInvalidToken;
/**
* 读取文件错误状态码
*/
extern const int kQNFileError;
/**
* 上传完成后返回的状态信息
*/
@interface QNResponseInfo : NSObject
/**
* 状态码
*/
@property (readonly) int statusCode;
/**
* 七牛服务器生成的请求ID用来跟踪请求信息如果使用过程中出现问题请反馈此ID
*/
@property (nonatomic, copy, readonly) NSString *reqId;
/**
* 七牛服务器内部跟踪记录
*/
@property (nonatomic, copy, readonly) NSString *xlog;
/**
* cdn服务器内部跟踪记录
*/
@property (nonatomic, copy, readonly) NSString *xvia;
/**
* 错误信息,出错时请反馈此记录
*/
@property (nonatomic, copy, readonly) NSError *error;
/**
* 服务器域名
*/
@property (nonatomic, copy, readonly) NSString *host;
/**
* 请求消耗的时间,单位 秒
*/
@property (nonatomic, readonly) double duration;
/**
* 服务器IP
*/
@property (nonatomic, readonly) NSString *serverIp;
/**
* 客户端id
*/
@property (nonatomic, readonly) NSString *id;
/**
* 时间戳
*/
@property (readonly) UInt64 timeStamp;
/**
* 网络类型
*/
//@property (nonatomic, readonly) NSString *networkType;
/**
* 是否取消
*/
@property (nonatomic, readonly, getter=isCancelled) BOOL canceled;
/**
* 成功的请求
*/
@property (nonatomic, readonly, getter=isOK) BOOL ok;
/**
* 是否网络错误
*/
@property (nonatomic, readonly, getter=isConnectionBroken) BOOL broken;
/**
* 是否需要重试,内部使用
*/
@property (nonatomic, readonly) BOOL couldRetry;
/**
* 是否需要换备用server内部使用
*/
@property (nonatomic, readonly) BOOL needSwitchServer;
/**
* 是否为 七牛响应
*/
@property (nonatomic, readonly, getter=isNotQiniu) BOOL notQiniu;
/**
* 工厂函数,内部使用
*
* @return 取消的实例
*/
+ (instancetype)cancel;
/**
* 工厂函数,内部使用
*
* @param desc 错误参数描述
*
* @return 错误参数实例
*/
+ (instancetype)responseInfoWithInvalidArgument:(NSString *)desc;
/**
* 工厂函数,内部使用
*
* @param desc 错误token描述
*
* @return 错误token实例
*/
+ (instancetype)responseInfoWithInvalidToken:(NSString *)desc;
/**
* 工厂函数,内部使用
*
* @param error 错误信息
* @param host 服务器域名
* @param duration 请求完成时间,单位秒
*
* @return 网络错误实例
*/
+ (instancetype)responseInfoWithNetError:(NSError *)error
host:(NSString *)host
duration:(double)duration;
/**
* 工厂函数,内部使用
*
* @param error 错误信息
*
* @return 文件错误实例
*/
+ (instancetype)responseInfoWithFileError:(NSError *)error;
/**
* 工厂函数,内部使用
*
* @return 文件错误实例
*/
+ (instancetype)responseInfoOfZeroData:(NSString *)path;
/**
* 构造函数
*
* @param status 状态码
* @param reqId 七牛服务器请求id
* @param xlog 七牛服务器记录
* @param body 服务器返回内容
* @param host 服务器域名
* @param duration 请求完成时间,单位秒
*
* @return 实例
*/
- (instancetype)init:(int)status
withReqId:(NSString *)reqId
withXLog:(NSString *)xlog
withXVia:(NSString *)xvia
withHost:(NSString *)host
withIp:(NSString *)ip
withDuration:(double)duration
withBody:(NSData *)body;
@end

View File

@@ -1,210 +0,0 @@
//
// QNResponseInfo.m
// QiniuSDK
//
// Created by bailong on 14/10/2.
// Copyright (c) 2014 Qiniu. All rights reserved.
//
#import "QNResponseInfo.h"
#import "QNUserAgent.h"
#import "QNVersion.h"
const int kQNZeroDataSize = -6;
const int kQNInvalidToken = -5;
const int kQNFileError = -4;
const int kQNInvalidArgument = -3;
const int kQNRequestCancelled = -2;
const int kQNNetworkError = -1;
/**
https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Constants/index.html#//apple_ref/doc/constant_group/URL_Loading_System_Error_Codes
NSURLErrorUnknown = -1,
NSURLErrorCancelled = -999,
NSURLErrorBadURL = -1000,
NSURLErrorTimedOut = -1001,
NSURLErrorUnsupportedURL = -1002,
NSURLErrorCannotFindHost = -1003,
NSURLErrorCannotConnectToHost = -1004,
NSURLErrorDataLengthExceedsMaximum = -1103,
NSURLErrorNetworkConnectionLost = -1005,
NSURLErrorDNSLookupFailed = -1006,
NSURLErrorHTTPTooManyRedirects = -1007,
NSURLErrorResourceUnavailable = -1008,
NSURLErrorNotConnectedToInternet = -1009,
NSURLErrorRedirectToNonExistentLocation = -1010,
NSURLErrorBadServerResponse = -1011,
NSURLErrorUserCancelledAuthentication = -1012,
NSURLErrorUserAuthenticationRequired = -1013,
NSURLErrorZeroByteResource = -1014,
NSURLErrorCannotDecodeRawData = -1015,
NSURLErrorCannotDecodeContentData = -1016,
NSURLErrorCannotParseResponse = -1017,
NSURLErrorInternationalRoamingOff = -1018,
NSURLErrorCallIsActive = -1019,
NSURLErrorDataNotAllowed = -1020,
NSURLErrorRequestBodyStreamExhausted = -1021,
NSURLErrorFileDoesNotExist = -1100,
NSURLErrorFileIsDirectory = -1101,
NSURLErrorNoPermissionsToReadFile = -1102,
NSURLErrorSecureConnectionFailed = -1200,
NSURLErrorServerCertificateHasBadDate = -1201,
NSURLErrorServerCertificateUntrusted = -1202,
NSURLErrorServerCertificateHasUnknownRoot = -1203,
NSURLErrorServerCertificateNotYetValid = -1204,
NSURLErrorClientCertificateRejected = -1205,
NSURLErrorClientCertificateRequired = -1206,
NSURLErrorCannotLoadFromNetwork = -2000,
NSURLErrorCannotCreateFile = -3000,
NSURLErrorCannotOpenFile = -3001,
NSURLErrorCannotCloseFile = -3002,
NSURLErrorCannotWriteToFile = -3003,
NSURLErrorCannotRemoveFile = -3004,
NSURLErrorCannotMoveFile = -3005,
NSURLErrorDownloadDecodingFailedMidStream = -3006,
NSURLErrorDownloadDecodingFailedToComplete = -3007
*/
static QNResponseInfo *cancelledInfo = nil;
static NSString *domain = @"qiniu.com";
@implementation QNResponseInfo
+ (instancetype)cancel {
return [[QNResponseInfo alloc] initWithCancelled];
}
+ (instancetype)responseInfoWithInvalidArgument:(NSString *)text {
return [[QNResponseInfo alloc] initWithStatus:kQNInvalidArgument errorDescription:text];
}
+ (instancetype)responseInfoWithInvalidToken:(NSString *)text {
return [[QNResponseInfo alloc] initWithStatus:kQNInvalidToken errorDescription:text];
}
+ (instancetype)responseInfoWithNetError:(NSError *)error host:(NSString *)host duration:(double)duration {
int code = kQNNetworkError;
if (error != nil) {
code = (int)error.code;
}
return [[QNResponseInfo alloc] initWithStatus:code error:error host:host duration:duration];
}
+ (instancetype)responseInfoWithFileError:(NSError *)error {
return [[QNResponseInfo alloc] initWithStatus:kQNFileError error:error];
}
+ (instancetype)responseInfoOfZeroData:(NSString *)path {
NSString *desc;
if (path == nil) {
desc = @"data size is 0";
} else {
desc = [[NSString alloc] initWithFormat:@"file %@ size is 0", path];
}
return [[QNResponseInfo alloc] initWithStatus:kQNZeroDataSize errorDescription:desc];
}
- (instancetype)initWithCancelled {
return [self initWithStatus:kQNRequestCancelled errorDescription:@"cancelled by user"];
}
- (instancetype)initWithStatus:(int)status
error:(NSError *)error {
return [self initWithStatus:status error:error host:nil duration:0];
}
- (instancetype)initWithStatus:(int)status
error:(NSError *)error
host:(NSString *)host
duration:(double)duration {
if (self = [super init]) {
_statusCode = status;
_error = error;
_host = host;
_duration = duration;
_id = [QNUserAgent sharedInstance].id;
_timeStamp = [[NSDate date] timeIntervalSince1970];
}
return self;
}
- (instancetype)initWithStatus:(int)status
errorDescription:(NSString *)text {
NSError *error = [[NSError alloc] initWithDomain:domain code:status userInfo:@{ @"error" : text }];
return [self initWithStatus:status error:error];
}
- (instancetype)init:(int)status
withReqId:(NSString *)reqId
withXLog:(NSString *)xlog
withXVia:(NSString *)xvia
withHost:(NSString *)host
withIp:(NSString *)ip
withDuration:(double)duration
withBody:(NSData *)body {
if (self = [super init]) {
_statusCode = status;
_reqId = [reqId copy];
_xlog = [xlog copy];
_xvia = [xvia copy];
_host = [host copy];
_duration = duration;
_serverIp = ip;
_id = [QNUserAgent sharedInstance].id;
_timeStamp = [[NSDate date] timeIntervalSince1970];
if (status != 200) {
if (body == nil) {
_error = [[NSError alloc] initWithDomain:domain code:_statusCode userInfo:nil];
} else {
NSError *tmp;
NSDictionary *uInfo = [NSJSONSerialization JSONObjectWithData:body options:NSJSONReadingMutableLeaves error:&tmp];
if (tmp != nil) {
// UTF8nil
NSString *str = [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding];
if (str == nil) {
str = @"";
}
uInfo = @{ @"error" : str };
}
_error = [[NSError alloc] initWithDomain:domain code:_statusCode userInfo:uInfo];
}
} else if (body == nil || body.length == 0) {
NSDictionary *uInfo = @{ @"error" : @"no response json" };
_error = [[NSError alloc] initWithDomain:domain code:_statusCode userInfo:uInfo];
}
}
return self;
}
- (NSString *)description {
return [NSString stringWithFormat:@"<%@= id: %@, ver: %@, status: %d, requestId: %@, xlog: %@, xvia: %@, host: %@ ip: %@ duration: %f s time: %llu error: %@>", NSStringFromClass([self class]), _id, kQiniuVersion, _statusCode, _reqId, _xlog, _xvia, _host, _serverIp, _duration, _timeStamp, _error];
}
- (BOOL)isCancelled {
return _statusCode == kQNRequestCancelled || _statusCode == -999;
}
- (BOOL)isNotQiniu {
return (_statusCode >= 200 && _statusCode < 500) && _reqId == nil;
}
- (BOOL)isOK {
return _statusCode == 200 && _error == nil && _reqId != nil;
}
- (BOOL)isConnectionBroken {
// reqId is nill means the server is not qiniu
return _statusCode == kQNNetworkError || (_statusCode < -1000 && _statusCode != -1003);
}
- (BOOL)needSwitchServer {
return _statusCode == kQNNetworkError || (_statusCode < -1000 && _statusCode != -1003) || (_statusCode / 100 == 5 && _statusCode != 579);
}
- (BOOL)couldRetry {
return (_statusCode >= 500 && _statusCode < 600 && _statusCode != 579) || _statusCode == kQNNetworkError || _statusCode == 996 || _statusCode == 406 || (_statusCode == 200 && _error != nil) || _statusCode < -1000 || self.isNotQiniu;
}
@end

View File

@@ -1,40 +0,0 @@
#import "QNHttpDelegate.h"
#import <Foundation/Foundation.h>
#import "QNConfiguration.h"
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090)
@interface QNSessionManager : NSObject <QNHttpDelegate>
- (instancetype)initWithProxy:(NSDictionary *)proxyDict
timeout:(UInt32)timeout
urlConverter:(QNUrlConvert)converter
dns:(QNDnsManager *)dns;
- (void)multipartPost:(NSString *)url
withData:(NSData *)data
withParams:(NSDictionary *)params
withFileName:(NSString *)key
withMimeType:(NSString *)mime
withCompleteBlock:(QNCompleteBlock)completeBlock
withProgressBlock:(QNInternalProgressBlock)progressBlock
withCancelBlock:(QNCancelBlock)cancelBlock
withAccess:(NSString *)access;
- (void)post:(NSString *)url
withData:(NSData *)data
withParams:(NSDictionary *)params
withHeaders:(NSDictionary *)headers
withCompleteBlock:(QNCompleteBlock)completeBlock
withProgressBlock:(QNInternalProgressBlock)progressBlock
withCancelBlock:(QNCancelBlock)cancelBlock
withAccess:(NSString *)access;
- (void)get:(NSString *)url
withHeaders:(NSDictionary *)headers
withCompleteBlock:(QNCompleteBlock)completeBlock;
@end
#endif

View File

@@ -1,333 +0,0 @@
//
// QNHttpManager.m
// QiniuSDK
//
// Created by bailong on 14/10/1.
// Copyright (c) 2014 Qiniu. All rights reserved.
//
#import "AFNetworking.h"
#import "HappyDNS.h"
#import "QNAsyncRun.h"
#import "QNConfiguration.h"
#import "QNResponseInfo.h"
#import "QNSessionManager.h"
#include "QNSystem.h"
#import "QNUserAgent.h"
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090)
@interface QNProgessDelegate : NSObject
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
@property (nonatomic, strong) QNInternalProgressBlock progressBlock;
@property (nonatomic, strong) NSProgress *progress;
@property (nonatomic, strong) NSURLSessionUploadTask *task;
@property (nonatomic, strong) QNCancelBlock cancelBlock;
- (instancetype)initWithProgress:(QNInternalProgressBlock)progressBlock;
@end
static NSURL *buildUrl(NSString *host, NSNumber *port, NSString *path) {
port = port == nil ? [NSNumber numberWithInt:80] : port;
NSString *p = [[NSString alloc] initWithFormat:@"http://%@:%@%@", host, port, path];
return [[NSURL alloc] initWithString:p];
}
static BOOL needRetry(NSHTTPURLResponse *httpResponse, NSError *error) {
if (error != nil) {
return error.code < -1000;
}
if (httpResponse == nil) {
return YES;
}
int status = (int)httpResponse.statusCode;
return status >= 500 && status < 600 && status != 579;
}
@implementation QNProgessDelegate
- (instancetype)initWithProgress:(QNInternalProgressBlock)progressBlock {
if (self = [super init]) {
_progressBlock = progressBlock;
_progress = nil;
}
return self;
}
- (void)valueChange:(NSProgress *)uploadProgress {
_progressBlock(uploadProgress.completedUnitCount, uploadProgress.totalUnitCount);
if (_cancelBlock && _cancelBlock()) {
[_task cancel];
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
{
if (context == nil || object == nil) {
return;
}
NSProgress *progress = (NSProgress *)object;
void *p = (__bridge void *)(self);
if (p == context) {
_progressBlock(progress.completedUnitCount, progress.totalUnitCount);
if (_cancelBlock && _cancelBlock()) {
[_task cancel];
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
@end
@interface QNSessionManager ()
@property (nonatomic) AFHTTPSessionManager *httpManager;
@property UInt32 timeout;
@property (nonatomic, strong) QNUrlConvert converter;
@property bool noProxy;
@property (nonatomic) QNDnsManager *dns;
@end
@implementation QNSessionManager
- (instancetype)initWithProxy:(NSDictionary *)proxyDict
timeout:(UInt32)timeout
urlConverter:(QNUrlConvert)converter
dns:(QNDnsManager *)dns {
if (self = [super init]) {
if (proxyDict != nil) {
_noProxy = NO;
} else {
_noProxy = YES;
}
_httpManager = [QNSessionManager httpManagerWithProxy:proxyDict];
_timeout = timeout;
_converter = converter;
_dns = dns;
}
return self;
}
+ (AFHTTPSessionManager *)httpManagerWithProxy:(NSDictionary *)proxyDict {
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
if (proxyDict != nil) {
configuration.connectionProxyDictionary = proxyDict;
}
AFHTTPSessionManager *httpManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:configuration];
httpManager.responseSerializer = [AFHTTPResponseSerializer serializer];
return httpManager;
}
- (instancetype)init {
return [self initWithProxy:nil timeout:60 urlConverter:nil dns:nil];
}
+ (QNResponseInfo *)buildResponseInfo:(NSHTTPURLResponse *)response
withError:(NSError *)error
withDuration:(double)duration
withResponse:(NSData *)body
withHost:(NSString *)host
withIp:(NSString *)ip {
QNResponseInfo *info;
if (response) {
int status = (int)[response statusCode];
NSDictionary *headers = [response allHeaderFields];
NSString *reqId = headers[@"X-Reqid"];
NSString *xlog = headers[@"X-Log"];
NSString *xvia = headers[@"X-Via"];
if (xvia == nil) {
xvia = headers[@"X-Px"];
}
if (xvia == nil) {
xvia = headers[@"Fw-Via"];
}
info = [[QNResponseInfo alloc] init:status withReqId:reqId withXLog:xlog withXVia:xvia withHost:host withIp:ip withDuration:duration withBody:body];
} else {
info = [QNResponseInfo responseInfoWithNetError:error host:host duration:duration];
}
return info;
}
- (void)sendRequest:(NSMutableURLRequest *)request
withCompleteBlock:(QNCompleteBlock)completeBlock
withProgressBlock:(QNInternalProgressBlock)progressBlock
withCancelBlock:(QNCancelBlock)cancelBlock
withAccess:(NSString *)access {
__block NSDate *startTime = [NSDate date];
NSString *domain = request.URL.host;
NSString *u = request.URL.absoluteString;
NSURL *url = request.URL;
NSArray *ips = nil;
if (_converter != nil) {
url = [[NSURL alloc] initWithString:_converter(u)];
request.URL = url;
domain = url.host;
} else if (_noProxy && _dns != nil && [url.scheme isEqualToString:@"http"]) {
if (isIpV6FullySupported() || ![QNIP isV6]) {
ips = [_dns queryWithDomain:[[QNDomain alloc] init:domain hostsFirst:NO hasCname:YES maxTtl:1000]];
double duration = [[NSDate date] timeIntervalSinceDate:startTime];
if (ips == nil || ips.count == 0) {
NSError *error = [[NSError alloc] initWithDomain:domain code:-1003 userInfo:@{ @"error" : @"unkonwn host" }];
QNResponseInfo *info = [QNResponseInfo responseInfoWithNetError:error host:domain duration:duration];
NSLog(@"failure %@", info);
completeBlock(info, nil);
return;
}
}
}
[self sendRequest2:request withCompleteBlock:completeBlock withProgressBlock:progressBlock withCancelBlock:cancelBlock withIpArray:ips withIndex:0 withDomain:domain withRetryTimes:3 withStartTime:startTime withAccess:access];
}
- (void)sendRequest2:(NSMutableURLRequest *)request
withCompleteBlock:(QNCompleteBlock)completeBlock
withProgressBlock:(QNInternalProgressBlock)progressBlock
withCancelBlock:(QNCancelBlock)cancelBlock
withIpArray:(NSArray *)ips
withIndex:(int)index
withDomain:(NSString *)domain
withRetryTimes:(int)times
withStartTime:(NSDate *)startTime
withAccess:(NSString *)access {
NSURL *url = request.URL;
__block NSString *ip = nil;
if (ips != nil) {
ip = [ips objectAtIndex:(index % ips.count)];
NSString *path = url.path;
if (path == nil || [@"" isEqualToString:path]) {
path = @"/";
}
url = buildUrl(ip, url.port, path);
[request setValue:domain forHTTPHeaderField:@"Host"];
}
request.URL = url;
[request setTimeoutInterval:_timeout];
[request setValue:[[QNUserAgent sharedInstance] getUserAgent:access] forHTTPHeaderField:@"User-Agent"];
[request setValue:nil forHTTPHeaderField:@"Accept-Language"];
if (progressBlock == nil) {
progressBlock = ^(long long totalBytesWritten, long long totalBytesExpectedToWrite) {
};
}
QNInternalProgressBlock progressBlock2 = ^(long long totalBytesWritten, long long totalBytesExpectedToWrite) {
progressBlock(totalBytesWritten, totalBytesExpectedToWrite);
};
__block QNProgessDelegate *delegate = [[QNProgessDelegate alloc] initWithProgress:progressBlock2];
NSURLSessionUploadTask *uploadTask = [_httpManager uploadTaskWithRequest:request fromData:nil progress:^(NSProgress *_Nonnull uploadProgress) {
[delegate valueChange:uploadProgress];
}
completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
NSData *data = responseObject;
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
double duration = [[NSDate date] timeIntervalSinceDate:startTime];
QNResponseInfo *info;
NSDictionary *resp = nil;
if (_converter != nil && _noProxy && (index + 1 < ips.count || times > 0) && needRetry(httpResponse, error)) {
[self sendRequest2:request withCompleteBlock:completeBlock withProgressBlock:progressBlock withCancelBlock:cancelBlock withIpArray:ips withIndex:index + 1 withDomain:domain withRetryTimes:times - 1 withStartTime:startTime withAccess:access];
return;
}
if (error == nil) {
info = [QNSessionManager buildResponseInfo:httpResponse withError:nil withDuration:duration withResponse:data withHost:domain withIp:ip];
if (info.isOK) {
NSError *tmp;
resp = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&tmp];
}
} else {
info = [QNSessionManager buildResponseInfo:httpResponse withError:error withDuration:duration withResponse:data withHost:domain withIp:ip];
}
completeBlock(info, resp);
}];
delegate.task = uploadTask;
delegate.cancelBlock = cancelBlock;
[uploadTask resume];
}
- (void)multipartPost:(NSString *)url
withData:(NSData *)data
withParams:(NSDictionary *)params
withFileName:(NSString *)key
withMimeType:(NSString *)mime
withCompleteBlock:(QNCompleteBlock)completeBlock
withProgressBlock:(QNInternalProgressBlock)progressBlock
withCancelBlock:(QNCancelBlock)cancelBlock
withAccess:(NSString *)access {
NSMutableURLRequest *request = [_httpManager.requestSerializer multipartFormRequestWithMethod:@"POST"
URLString:url
parameters:params
constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:data name:@"file" fileName:key mimeType:mime];
}
error:nil];
[self sendRequest:request withCompleteBlock:completeBlock withProgressBlock:progressBlock withCancelBlock:cancelBlock
withAccess:access];
}
- (void)post:(NSString *)url
withData:(NSData *)data
withParams:(NSDictionary *)params
withHeaders:(NSDictionary *)headers
withCompleteBlock:(QNCompleteBlock)completeBlock
withProgressBlock:(QNInternalProgressBlock)progressBlock
withCancelBlock:(QNCancelBlock)cancelBlock
withAccess:(NSString *)access {
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[[NSURL alloc] initWithString:url]];
if (headers) {
[request setAllHTTPHeaderFields:headers];
}
[request setHTTPMethod:@"POST"];
if (params) {
[request setValuesForKeysWithDictionary:params];
}
[request setHTTPBody:data];
QNAsyncRun(^{
[self sendRequest:request
withCompleteBlock:completeBlock
withProgressBlock:progressBlock
withCancelBlock:cancelBlock
withAccess:access];
});
}
- (void)get:(NSString *)url
withHeaders:(NSDictionary *)headers
withCompleteBlock:(QNCompleteBlock)completeBlock {
QNAsyncRun(^{
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:url];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSData *s = [@"{}" dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *resp = nil;
QNResponseInfo *info;
if (error == nil) {
info = [QNSessionManager buildResponseInfo:httpResponse withError:nil withDuration:0 withResponse:s withHost:@"" withIp:@""];
if (info.isOK) {
resp = responseObject;
}
} else {
info = [QNSessionManager buildResponseInfo:httpResponse withError:error withDuration:0 withResponse:s withHost:@"" withIp:@""];
}
completeBlock(info, resp);
}];
[dataTask resume];
});
}
@end
#endif

View File

@@ -1,37 +0,0 @@
//
// QNUserAgent.h
// QiniuSDK
//
// Created by bailong on 14-9-29.
// Copyright (c) 2014年 Qiniu. All rights reserved.
//
#import <Foundation/Foundation.h>
/**
* UserAgent
*
*/
@interface QNUserAgent : NSObject
/**
* 用户id
*/
@property (copy, nonatomic, readonly) NSString *id;
/**
* UserAgent 字串
*/
- (NSString *)description;
/**
* UserAgent + AK 字串
*/
- (NSString *)getUserAgent:(NSString *)access;
/**
* 单例
*/
+ (instancetype)sharedInstance;
@end

View File

@@ -1,86 +0,0 @@
//
// QNUserAgent.m
// QiniuSDK
//
// Created by bailong on 14-9-29.
// Copyright (c) 2014 Qiniu. All rights reserved.
//
#import <Foundation/Foundation.h>
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#import <MobileCoreServices/MobileCoreServices.h>
#import <UIKit/UIKit.h>
#else
#import <CoreServices/CoreServices.h>
#endif
#import "QNUserAgent.h"
#import "QNVersion.h"
static NSString *qn_clientId(void) {
#if __IPHONE_OS_VERSION_MIN_REQUIRED
NSString *s = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
if (s == nil) {
s = @"simulator";
}
return s;
#else
long long now_timestamp = [[NSDate date] timeIntervalSince1970] * 1000;
int r = arc4random() % 1000;
return [NSString stringWithFormat:@"%lld%u", now_timestamp, r];
#endif
}
static NSString *qn_userAgent(NSString *id, NSString *ak) {
#if __IPHONE_OS_VERSION_MIN_REQUIRED
return [NSString stringWithFormat:@"QiniuObject-C/%@ (%@; iOS %@; %@; %@)", kQiniuVersion, [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], id, ak];
#else
return [NSString stringWithFormat:@"QiniuObject-C/%@ (Mac OS X %@; %@; %@)", kQiniuVersion, [[NSProcessInfo processInfo] operatingSystemVersionString], id, ak];
#endif
}
@interface QNUserAgent ()
@property (nonatomic) NSString *ua;
@end
@implementation QNUserAgent
- (NSString *)description {
return _ua;
}
- (instancetype)init {
if (self = [super init]) {
_id = qn_clientId();
}
return self;
}
/**
* UserAgent
*/
- (NSString *)getUserAgent:(NSString *)access {
NSString *ak;
if (access == nil || access.length == 0) {
ak = @"-";
} else {
ak = access;
}
return qn_userAgent(_id, ak);
}
/**
*
*/
+ (instancetype)sharedInstance {
static QNUserAgent *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
@end

View File

@@ -1,16 +0,0 @@
//
// QiniuSDK.h
// QiniuSDK
//
// Created by bailong on 14-9-28.
// Copyright (c) 2014年 Qiniu. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "QNConfiguration.h"
#import "QNFileRecorder.h"
#import "QNResponseInfo.h"
#import "QNUploadManager.h"
#import "QNUploadOption.h"
#import "QNUrlSafeBase64.h"

View File

@@ -1,52 +0,0 @@
//
// QNFileRecorder.h
// QiniuSDK
//
// Created by bailong on 14/10/5.
// Copyright (c) 2014年 Qiniu. All rights reserved.
//
#import "QNRecorderDelegate.h"
#import <Foundation/Foundation.h>
/**
* 将上传记录保存到文件系统中
*/
@interface QNFileRecorder : NSObject <QNRecorderDelegate>
/**
* 用指定保存的目录进行初始化
*
* @param directory 目录
* @param error 输出的错误信息
*
* @return 实例
*/
+ (instancetype)fileRecorderWithFolder:(NSString *)directory
error:(NSError *__autoreleasing *)error;
/**
* 用指定保存的目录以及是否进行base64编码进行初始化
*
* @param directory 目录
* @param encode 为避免因为特殊字符或含有/无法保存持久化记录故用此参数指定是否要base64编码
* @param error 输出错误信息
*
* @return 实例
*/
+ (instancetype)fileRecorderWithFolder:(NSString *)directory
encodeKey:(BOOL)encode
error:(NSError *__autoreleasing *)error;
/**
* 从外部手动删除记录,如无特殊需求,不建议使用
*
* @param key 持久化记录key
* @param dir 目录
* @param encode 是否encode
*/
+ (void)removeKey:(NSString *)key
directory:(NSString *)dir
encodeKey:(BOOL)encode;
@end

View File

@@ -1,99 +0,0 @@
//
// QNFileRecorder.m
// QiniuSDK
//
// Created by bailong on 14/10/5.
// Copyright (c) 2014 Qiniu. All rights reserved.
//
#import "QNFileRecorder.h"
#import "QNUrlSafeBase64.h"
@interface QNFileRecorder ()
@property (copy, readonly) NSString *directory;
@property BOOL encode;
@end
@implementation QNFileRecorder
- (NSString *)pathOfKey:(NSString *)key {
return [QNFileRecorder pathJoin:key path:_directory];
}
+ (NSString *)pathJoin:(NSString *)key
path:(NSString *)path {
return [[NSString alloc] initWithFormat:@"%@/%@", path, key];
}
+ (instancetype)fileRecorderWithFolder:(NSString *)directory
error:(NSError *__autoreleasing *)perror {
return [QNFileRecorder fileRecorderWithFolder:directory encodeKey:false error:perror];
}
+ (instancetype)fileRecorderWithFolder:(NSString *)directory
encodeKey:(BOOL)encode
error:(NSError *__autoreleasing *)perror {
NSError *error;
[[NSFileManager defaultManager] createDirectoryAtPath:directory withIntermediateDirectories:YES attributes:nil error:&error];
if (error != nil) {
if (perror) {
*perror = error;
}
return nil;
}
return [[QNFileRecorder alloc] initWithFolder:directory encodeKey:encode];
}
- (instancetype)initWithFolder:(NSString *)directory encodeKey:(BOOL)encode {
if (self = [super init]) {
_directory = directory;
_encode = encode;
}
return self;
}
- (NSError *)set:(NSString *)key
data:(NSData *)value {
NSError *error;
if (_encode) {
key = [QNUrlSafeBase64 encodeString:key];
}
[value writeToFile:[self pathOfKey:key] options:NSDataWritingAtomic error:&error];
return error;
}
- (NSData *)get:(NSString *)key {
if (_encode) {
key = [QNUrlSafeBase64 encodeString:key];
}
return [NSData dataWithContentsOfFile:[self pathOfKey:key]];
}
- (NSError *)del:(NSString *)key {
NSError *error;
if (_encode) {
key = [QNUrlSafeBase64 encodeString:key];
}
[[NSFileManager defaultManager] removeItemAtPath:[self pathOfKey:key] error:&error];
return error;
}
+ (void)removeKey:(NSString *)key
directory:(NSString *)dir
encodeKey:(BOOL)encode {
if (encode) {
key = [QNUrlSafeBase64 encodeString:key];
}
NSError *error;
NSString *path = [QNFileRecorder pathJoin:key path:dir];
[[NSFileManager defaultManager] removeItemAtPath:path error:&error];
}
- (NSString *)description {
return [NSString stringWithFormat:@"<%@: %p, dir: %@>", NSStringFromClass([self class]), self, _directory];
}
@end

View File

@@ -1,55 +0,0 @@
//
// QNRecorderDelegate.h
// QiniuSDK
//
// Created by bailong on 14/10/5.
// Copyright (c) 2014年 Qiniu. All rights reserved.
//
#import <Foundation/Foundation.h>
/**
* 为持久化上传记录根据上传的key以及文件名 生成持久化的记录key
*
* @param uploadKey 上传的key
* @param filePath 文件名
*
* @return 根据uploadKey, filepath 算出的记录key
*/
typedef NSString * (^QNRecorderKeyGenerator)(NSString *uploadKey, NSString *filePath);
/**
* 持久化记录接口,可以实现将记录持久化到文件,数据库等
*/
@protocol QNRecorderDelegate <NSObject>
/**
* 保存记录
*
* @param key 持久化记录的key
* @param value 持久化记录上传状态信息
*
* @return 错误信息成功为nil
*/
- (NSError *)set:(NSString *)key
data:(NSData *)value;
/**
* 取出保存的持久化上传状态信息
*
* @param key 持久化记录key
*
* @return 上传中间状态信息
*/
- (NSData *)get:(NSString *)key;
/**
* 删除持久化记录,一般在上传成功后自动调用
*
* @param key 持久化记录key
*
* @return 错误信息成功为nil
*/
- (NSError *)del:(NSString *)key;
@end

View File

@@ -1,205 +0,0 @@
//
// QNConfiguration.h
// QiniuSDK
//
// Created by bailong on 15/5/21.
// Copyright (c) 2015年 Qiniu. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "QNRecorderDelegate.h"
/**
* 断点上传时的分块大小
*/
extern const UInt32 kQNBlockSize;
/**
* 转换为用户需要的url
*
* @param url 上传url
*
* @return 根据上传url算出代理url
*/
typedef NSString * (^QNUrlConvert)(NSString *url);
@class QNConfigurationBuilder;
@class QNDnsManager;
@class QNServiceAddress;
@class QNZone;
/**
* Builder block
*
* @param builder builder实例
*/
typedef void (^QNConfigurationBuilderBlock)(QNConfigurationBuilder *builder);
@interface QNConfiguration : NSObject
/**
* 存储区域
*/
@property (copy, nonatomic, readonly) QNZone *zone;
/**
* 断点上传时的分片大小
*/
@property (readonly) UInt32 chunkSize;
/**
* 如果大于此值就使用断点上传否则使用form上传
*/
@property (readonly) UInt32 putThreshold;
/**
* 上传失败的重试次数
*/
@property (readonly) UInt32 retryMax;
/**
* 超时时间 单位 秒
*/
@property (readonly) UInt32 timeoutInterval;
@property (nonatomic, readonly) id<QNRecorderDelegate> recorder;
@property (nonatomic, readonly) QNRecorderKeyGenerator recorderKeyGen;
@property (nonatomic, readonly) NSDictionary *proxy;
@property (nonatomic, readonly) QNUrlConvert converter;
@property (nonatomic, readonly) QNDnsManager *dns;
@property (readonly) BOOL disableATS;
+ (instancetype)build:(QNConfigurationBuilderBlock)block;
@end
/**
* 上传服务地址
*/
@interface QNServiceAddress : NSObject
- (instancetype)init:(NSString *)address ips:(NSArray *)ips;
@property (nonatomic, readonly) NSString *address;
@property (nonatomic, readonly) NSArray *ips;
@end
typedef void (^QNPrequeryReturn)(int code);
@class QNUpToken;
@interface QNZone : NSObject
/**
* 默认上传服务器地址
*/
- (QNServiceAddress *)up:(QNUpToken *)token;
/**
* 备用上传服务器地址
*/
- (QNServiceAddress *)upBackup:(QNUpToken *)token;
/**
* zone 0 华东
*
* @return 实例
*/
+ (instancetype)zone0;
/**
* zone 1 华北
*
* @return 实例
*/
+ (instancetype)zone1;
/**
* zone 2 华南
*
* @return 实例
*/
+ (instancetype)zone2;
/**
* zone Na0 北美
*
* @return 实例
*/
+ (instancetype)zoneNa0;
- (void)preQuery:(QNUpToken *)token
on:(QNPrequeryReturn)ret;
+ (void)addIpToDns:(QNDnsManager *)dns;
@end
@interface QNFixedZone : QNZone
/**
* Zone初始化方法
*
* @param upHost 默认上传服务器地址
* @param upHostBackup 备用上传服务器地址
* @param upIp 备用上传IP
*
* @return Zone实例
*/
- (instancetype)initWithUp:(QNServiceAddress *)up
upBackup:(QNServiceAddress *)upBackup;
@end
@interface QNAutoZone : QNZone
- (instancetype)initWithHttps:(BOOL)flag
dns:(QNDnsManager *)dns;
@end
@interface QNConfigurationBuilder : NSObject
/**
* 默认上传服务器地址
*/
@property (nonatomic, strong) QNZone *zone;
/**
* 断点上传时的分片大小
*/
@property (assign) UInt32 chunkSize;
/**
* 如果大于此值就使用断点上传否则使用form上传
*/
@property (assign) UInt32 putThreshold;
/**
* 上传失败的重试次数
*/
@property (assign) UInt32 retryMax;
/**
* 超时时间 单位 秒
*/
@property (assign) UInt32 timeoutInterval;
@property (nonatomic, strong) id<QNRecorderDelegate> recorder;
@property (nonatomic, strong) QNRecorderKeyGenerator recorderKeyGen;
@property (nonatomic, strong) NSDictionary *proxy;
@property (nonatomic, strong) QNUrlConvert converter;
@property (nonatomic, strong) QNDnsManager *dns;
@property (assign) BOOL disableATS;
@end

View File

@@ -1,360 +0,0 @@
//
// QNConfiguration.m
// QiniuSDK
//
// Created by bailong on 15/5/21.
// Copyright (c) 2015 Qiniu. All rights reserved.
//
#import "QNConfiguration.h"
#import "HappyDNS.h"
#import "QNNetworkInfo.h"
#import "QNResponseInfo.h"
#import "QNSessionManager.h"
#import "QNSystem.h"
#import "QNUpToken.h"
const UInt32 kQNBlockSize = 4 * 1024 * 1024;
static void addServiceToDns(QNServiceAddress *address, QNDnsManager *dns) {
NSArray *ips = address.ips;
if (ips == nil) {
return;
}
NSURL *u = [[NSURL alloc] initWithString:address.address];
NSString *host = u.host;
for (int i = 0; i < ips.count; i++) {
[dns putHosts:host ip:ips[i]];
}
}
static void addZoneToDns(QNZone *zone, QNDnsManager *dns) {
addServiceToDns([zone up:nil], dns);
addServiceToDns([zone upBackup:nil], dns);
}
static QNDnsManager *initDns(QNConfigurationBuilder *builder) {
QNDnsManager *d = builder.dns;
if (d == nil) {
id<QNResolverDelegate> r1 = [QNResolver systemResolver];
id<QNResolverDelegate> r2 = [[QNResolver alloc] initWithAddress:@"119.29.29.29"];
id<QNResolverDelegate> r3 = [[QNResolver alloc] initWithAddress:@"114.114.115.115"];
d = [[QNDnsManager alloc] init:[NSArray arrayWithObjects:r1, r2, r3, nil] networkInfo:[QNNetworkInfo normal]];
}
return d;
}
@implementation QNConfiguration
+ (instancetype)build:(QNConfigurationBuilderBlock)block {
QNConfigurationBuilder *builder = [[QNConfigurationBuilder alloc] init];
block(builder);
return [[QNConfiguration alloc] initWithBuilder:builder];
}
- (instancetype)initWithBuilder:(QNConfigurationBuilder *)builder {
if (self = [super init]) {
_chunkSize = builder.chunkSize;
_putThreshold = builder.putThreshold;
_retryMax = builder.retryMax;
_timeoutInterval = builder.timeoutInterval;
_recorder = builder.recorder;
_recorderKeyGen = builder.recorderKeyGen;
_proxy = builder.proxy;
_converter = builder.converter;
_disableATS = builder.disableATS;
if (_disableATS) {
_dns = initDns(builder);
[QNZone addIpToDns:_dns];
} else {
_dns = nil;
}
_zone = builder.zone;
}
return self;
}
@end
@implementation QNConfigurationBuilder
- (instancetype)init {
if (self = [super init]) {
_zone = [QNZone zone0];
_chunkSize = 256 * 1024;
_putThreshold = 512 * 1024;
_retryMax = 2;
_timeoutInterval = 60;
_recorder = nil;
_recorderKeyGen = nil;
_proxy = nil;
_converter = nil;
if (hasAts() && !allowsArbitraryLoads()) {
_disableATS = NO;
} else {
_disableATS = YES;
}
}
return self;
}
@end
@implementation QNServiceAddress : NSObject
- (instancetype)init:(NSString *)address ips:(NSArray *)ips {
if (self = [super init]) {
_address = address;
_ips = ips;
}
return self;
}
@end
@implementation QNFixedZone {
QNServiceAddress *up;
QNServiceAddress *upBackup;
}
/**
*
*/
- (QNServiceAddress *)upBackup:(NSString *)token {
return upBackup;
}
- (QNServiceAddress *)up:(NSString *)token {
return up;
}
- (instancetype)initWithUp:(QNServiceAddress *)up1
upBackup:(QNServiceAddress *)upBackup1 {
if (self = [super init]) {
up = up1;
upBackup = upBackup1;
}
return self;
}
@end
@interface QNAutoZoneInfo : NSObject
@property (readonly, nonatomic) NSString *upHost;
@property (readonly, nonatomic) NSString *upIp;
@property (readonly, nonatomic) NSString *upBackup;
@property (readonly, nonatomic) NSString *upHttps;
- (instancetype)init:(NSString *)uphost
upIp:(NSString *)upip
upBackup:(NSString *)upbackup
upHttps:(NSString *)uphttps;
@end
@implementation QNAutoZoneInfo
- (instancetype)init:(NSString *)uphost
upIp:(NSString *)upip
upBackup:(NSString *)upbackup
upHttps:(NSString *)uphttps {
if (self = [super init]) {
_upHost = uphost;
_upIp = upip;
_upBackup = upbackup;
_upHttps = uphttps;
}
return self;
}
@end
@implementation QNAutoZone {
NSString *server;
BOOL https;
NSMutableDictionary *cache;
NSLock *lock;
QNSessionManager *sesionManager;
QNDnsManager *dns;
}
- (instancetype)initWithHttps:(BOOL)flag
dns:(QNDnsManager *)dns1 {
if (self = [super init]) {
dns = dns1;
server = @"https://uc.qbox.me";
https = flag;
cache = [NSMutableDictionary new];
lock = [NSLock new];
sesionManager = [[QNSessionManager alloc] initWithProxy:nil timeout:10 urlConverter:nil dns:dns];
}
return self;
}
- (QNServiceAddress *)upBackup:(QNUpToken *)token {
NSString *index = [token index];
[lock lock];
QNAutoZoneInfo *info = [cache objectForKey:index];
[lock unlock];
if (info == nil) {
return nil;
}
if (https) {
return [[QNServiceAddress alloc] init:info.upHttps ips:@[ info.upIp ]];
}
return [[QNServiceAddress alloc] init:info.upBackup ips:@[ info.upIp ]];
}
- (QNServiceAddress *)up:(QNUpToken *)token {
NSString *index = [token index];
[lock lock];
QNAutoZoneInfo *info = [cache objectForKey:index];
[lock unlock];
if (info == nil) {
return nil;
}
if (https) {
return [[QNServiceAddress alloc] init:info.upHttps ips:@[ info.upIp ]];
}
return [[QNServiceAddress alloc] init:info.upHost ips:@[ info.upIp ]];
}
- (QNAutoZoneInfo *)buildInfoFromJson:(NSDictionary *)resp {
NSDictionary *http = [resp objectForKey:@"http"];
NSArray *up = [http objectForKey:@"up"];
NSString *upHost = [up objectAtIndex:1];
NSString *upBackup = [up objectAtIndex:0];
NSString *ipTemp = [up objectAtIndex:2];
NSArray *a1 = [ipTemp componentsSeparatedByString:@" "];
NSString *ip1 = [a1 objectAtIndex:2];
NSArray *a2 = [ip1 componentsSeparatedByString:@"//"];
NSString *upIp = [a2 objectAtIndex:1];
NSDictionary *https_ = [resp objectForKey:@"https"];
NSArray *a3 = [https_ objectForKey:@"up"];
NSString *upHttps = [a3 objectAtIndex:0];
return [[QNAutoZoneInfo alloc] init:upHost upIp:upIp upBackup:upBackup upHttps:upHttps];
}
- (void)preQuery:(QNUpToken *)token
on:(QNPrequeryReturn)ret {
if (token == nil) {
ret(-1);
}
[lock lock];
QNAutoZoneInfo *info = [cache objectForKey:[token index]];
[lock unlock];
if (info != nil) {
ret(0);
return;
}
NSString *url = [NSString stringWithFormat:@"%@/v1/query?ak=%@&bucket=%@", server, token.access, token.bucket];
[sesionManager get:url withHeaders:nil withCompleteBlock:^(QNResponseInfo *info, NSDictionary *resp) {
if ([info isOK]) {
QNAutoZoneInfo *info = [self buildInfoFromJson:resp];
if (info == nil) {
ret(kQNInvalidToken);
} else {
ret(0);
[lock lock];
[cache setValue:info forKey:[token index]];
[lock unlock];
if (dns != nil) {
QNServiceAddress *address = [[QNServiceAddress alloc] init:info.upHttps ips:@[ info.upIp ]];
addServiceToDns(address, dns);
address = [[QNServiceAddress alloc] init:info.upHost ips:@[ info.upIp ]];
addServiceToDns(address, dns);
address = [[QNServiceAddress alloc] init:info.upBackup ips:@[ info.upIp ]];
addServiceToDns(address, dns);
}
}
} else {
ret(kQNNetworkError);
}
}];
}
@end
@implementation QNZone
- (instancetype)init {
self = [super init];
return self;
}
/**
*
*/
- (QNServiceAddress *)upBackup:(QNUpToken *)token {
return nil;
}
- (QNServiceAddress *)up:(QNUpToken *)token {
return nil;
}
+ (instancetype)createWithHost:(NSString *)up backupHost:(NSString *)backup ip1:(NSString *)ip1 ip2:(NSString *)ip2 {
NSArray *ips = [NSArray arrayWithObjects:ip1, ip2, nil];
NSString *a = [NSString stringWithFormat:@"http://%@", up];
QNServiceAddress *s1 = [[QNServiceAddress alloc] init:a ips:ips];
NSString *b = [NSString stringWithFormat:@"http://%@", backup];
QNServiceAddress *s2 = [[QNServiceAddress alloc] init:b ips:ips];
return [[QNFixedZone alloc] initWithUp:s1 upBackup:s2];
}
+ (instancetype)zone0 {
static QNZone *z0 = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
z0 = [QNZone createWithHost:@"upload.qiniu.com" backupHost:@"up.qiniu.com" ip1:@"183.136.139.10" ip2:@"115.231.182.136"];
});
return z0;
}
+ (instancetype)zone1 {
static QNZone *z1 = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
z1 = [QNZone createWithHost:@"upload-z1.qiniu.com" backupHost:@"up-z1.qiniu.com" ip1:@"106.38.227.28" ip2:@"106.38.227.27"];
});
return z1;
}
+ (instancetype)zone2 {
static QNZone *z2 = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
z2 = [QNZone createWithHost:@"upload-z2.qiniu.com" backupHost:@"up-z2.qiniu.com" ip1:@"14.152.37.7" ip2:@"183.60.214.199"];
});
return z2;
}
+ (instancetype)zoneNa0 {
static QNZone *zNa0 = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
zNa0 = [QNZone createWithHost:@"upload-na0.qiniu.com" backupHost:@"up-na0.qiniu.com" ip1:@"14.152.37.7" ip2:@"183.60.214.199"];
});
return zNa0;
}
+ (void)addIpToDns:(QNDnsManager *)dns {
addZoneToDns([QNZone zone0], dns);
addZoneToDns([QNZone zone1], dns);
addZoneToDns([QNZone zone2], dns);
}
- (void)preQuery:(QNUpToken *)token
on:(QNPrequeryReturn)ret {
ret(0);
}
@end

View File

@@ -1,26 +0,0 @@
//
// QNFormUpload.h
// QiniuSDK
//
// Created by bailong on 15/1/4.
// Copyright (c) 2015年 Qiniu. All rights reserved.
//
#import "QNHttpDelegate.h"
#import "QNUpToken.h"
#import "QNUploadManager.h"
#import <Foundation/Foundation.h>
@interface QNFormUpload : NSObject
- (instancetype)initWithData:(NSData *)data
withKey:(NSString *)key
withToken:(QNUpToken *)token
withCompletionHandler:(QNUpCompletionHandler)block
withOption:(QNUploadOption *)option
withHttpManager:(id<QNHttpDelegate>)http
withConfiguration:(QNConfiguration *)config;
- (void)put;
@end

View File

@@ -1,133 +0,0 @@
//
// QNFormUpload.m
// QiniuSDK
//
// Created by bailong on 15/1/4.
// Copyright (c) 2015 Qiniu. All rights reserved.
//
#import "QNFormUpload.h"
#import "QNConfiguration.h"
#import "QNCrc32.h"
#import "QNRecorderDelegate.h"
#import "QNResponseInfo.h"
#import "QNUploadManager.h"
#import "QNUploadOption+Private.h"
#import "QNUrlSafeBase64.h"
@interface QNFormUpload ()
@property (nonatomic, strong) NSData *data;
@property (nonatomic, strong) id<QNHttpDelegate> httpManager;
@property (nonatomic) int retryTimes;
@property (nonatomic, strong) NSString *key;
@property (nonatomic, strong) QNUpToken *token;
@property (nonatomic, strong) QNUploadOption *option;
@property (nonatomic, strong) QNUpCompletionHandler complete;
@property (nonatomic, strong) QNConfiguration *config;
@property (nonatomic) float previousPercent;
@property (nonatomic, strong) NSString *access; //AK
@end
@implementation QNFormUpload
- (instancetype)initWithData:(NSData *)data
withKey:(NSString *)key
withToken:(QNUpToken *)token
withCompletionHandler:(QNUpCompletionHandler)block
withOption:(QNUploadOption *)option
withHttpManager:(id<QNHttpDelegate>)http
withConfiguration:(QNConfiguration *)config {
if (self = [super init]) {
_data = data;
_key = key;
_token = token;
_option = option != nil ? option : [QNUploadOption defaultOptions];
_complete = block;
_httpManager = http;
_config = config;
_previousPercent = 0;
_access = token.access;
}
return self;
}
- (void)put {
NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
NSString *fileName = _key;
if (_key) {
parameters[@"key"] = _key;
} else {
fileName = @"?";
}
parameters[@"token"] = _token.token;
[parameters addEntriesFromDictionary:_option.params];
if (_option.checkCrc) {
parameters[@"crc32"] = [NSString stringWithFormat:@"%u", (unsigned int)[QNCrc32 data:_data]];
}
QNInternalProgressBlock p = ^(long long totalBytesWritten, long long totalBytesExpectedToWrite) {
float percent = (float)totalBytesWritten / (float)totalBytesExpectedToWrite;
if (percent > 0.95) {
percent = 0.95;
}
if (percent > _previousPercent) {
_previousPercent = percent;
} else {
percent = _previousPercent;
}
_option.progressHandler(_key, percent);
};
QNCompleteBlock complete = ^(QNResponseInfo *info, NSDictionary *resp) {
if (info.isOK) {
_option.progressHandler(_key, 1.0);
}
if (info.isOK || !info.couldRetry) {
_complete(info, _key, resp);
return;
}
if (_option.cancellationSignal()) {
_complete([QNResponseInfo cancel], _key, nil);
return;
}
NSString *nextHost = [_config.zone up:_token].address;
if (info.isConnectionBroken || info.needSwitchServer) {
nextHost = [_config.zone upBackup:_token].address;
}
QNCompleteBlock retriedComplete = ^(QNResponseInfo *info, NSDictionary *resp) {
if (info.isOK) {
_option.progressHandler(_key, 1.0);
}
_complete(info, _key, resp);
};
[_httpManager multipartPost:nextHost
withData:_data
withParams:parameters
withFileName:fileName
withMimeType:_option.mimeType
withCompleteBlock:retriedComplete
withProgressBlock:p
withCancelBlock:_option.cancellationSignal
withAccess:_access];
};
[_httpManager multipartPost:[_config.zone up:_token].address
withData:_data
withParams:parameters
withFileName:fileName
withMimeType:_option.mimeType
withCompleteBlock:complete
withProgressBlock:p
withCancelBlock:_option.cancellationSignal
withAccess:_access];
}
@end

View File

@@ -1,29 +0,0 @@
//
// QNResumeUpload.h
// QiniuSDK
//
// Created by bailong on 14/10/1.
// Copyright (c) 2014年 Qiniu. All rights reserved.
//
#import "QNFileDelegate.h"
#import "QNHttpDelegate.h"
#import "QNUpToken.h"
#import "QNUploadManager.h"
#import <Foundation/Foundation.h>
@interface QNResumeUpload : NSObject
- (instancetype)initWithFile:(id<QNFileDelegate>)file
withKey:(NSString *)key
withToken:(QNUpToken *)token
withCompletionHandler:(QNUpCompletionHandler)block
withOption:(QNUploadOption *)option
withRecorder:(id<QNRecorderDelegate>)recorder
withRecorderKey:(NSString *)recorderKey
withHttpManager:(id<QNHttpDelegate>)http
withConfiguration:(QNConfiguration *)config;
- (void)run;
@end

View File

@@ -1,337 +0,0 @@
//
// QNResumeUpload.m
// QiniuSDK
//
// Created by bailong on 14/10/1.
// Copyright (c) 2014 Qiniu. All rights reserved.
//
#import "QNResumeUpload.h"
#import "QNConfiguration.h"
#import "QNCrc32.h"
#import "QNRecorderDelegate.h"
#import "QNResponseInfo.h"
#import "QNUploadManager.h"
#import "QNUploadOption+Private.h"
#import "QNUrlSafeBase64.h"
typedef void (^task)(void);
@interface QNResumeUpload ()
@property (nonatomic, strong) id<QNHttpDelegate> httpManager;
@property UInt32 size;
@property (nonatomic) int retryTimes;
@property (nonatomic, strong) NSString *key;
@property (nonatomic, strong) NSString *recorderKey;
@property (nonatomic) NSDictionary *headers;
@property (nonatomic, strong) QNUploadOption *option;
@property (nonatomic, strong) QNUpToken *token;
@property (nonatomic, strong) QNUpCompletionHandler complete;
@property (nonatomic, strong) NSMutableArray *contexts;
@property int64_t modifyTime;
@property (nonatomic, strong) id<QNRecorderDelegate> recorder;
@property (nonatomic, strong) QNConfiguration *config;
@property UInt32 chunkCrc;
@property (nonatomic, strong) id<QNFileDelegate> file;
//@property (nonatomic, strong) NSArray *fileAry;
@property (nonatomic) float previousPercent;
@property (nonatomic, strong) NSString *access; //AK
- (void)makeBlock:(NSString *)uphost
offset:(UInt32)offset
blockSize:(UInt32)blockSize
chunkSize:(UInt32)chunkSize
progress:(QNInternalProgressBlock)progressBlock
complete:(QNCompleteBlock)complete;
- (void)putChunk:(NSString *)uphost
offset:(UInt32)offset
size:(UInt32)size
context:(NSString *)context
progress:(QNInternalProgressBlock)progressBlock
complete:(QNCompleteBlock)complete;
- (void)makeFile:(NSString *)uphost
complete:(QNCompleteBlock)complete;
@end
@implementation QNResumeUpload
- (instancetype)initWithFile:(id<QNFileDelegate>)file
withKey:(NSString *)key
withToken:(QNUpToken *)token
withCompletionHandler:(QNUpCompletionHandler)block
withOption:(QNUploadOption *)option
withRecorder:(id<QNRecorderDelegate>)recorder
withRecorderKey:(NSString *)recorderKey
withHttpManager:(id<QNHttpDelegate>)http
withConfiguration:(QNConfiguration *)config;
{
if (self = [super init]) {
_file = file;
_size = (UInt32)[file size];
_key = key;
NSString *tokenUp = [NSString stringWithFormat:@"UpToken %@", token.token];
_option = option != nil ? option : [QNUploadOption defaultOptions];
_complete = block;
_headers = @{@"Authorization" : tokenUp, @"Content-Type" : @"application/octet-stream"};
_recorder = recorder;
_httpManager = http;
_modifyTime = [file modifyTime];
_recorderKey = recorderKey;
_contexts = [[NSMutableArray alloc] initWithCapacity:(_size + kQNBlockSize - 1) / kQNBlockSize];
_config = config;
_token = token;
_previousPercent = 0;
_access = token.access;
}
return self;
}
// save json value
//{
// "size":filesize,
// "offset":lastSuccessOffset,
// "modify_time": lastFileModifyTime,
// "contexts": contexts
//}
- (void)record:(UInt32)offset {
NSString *key = self.recorderKey;
if (offset == 0 || _recorder == nil || key == nil || [key isEqualToString:@""]) {
return;
}
NSNumber *n_size = @(self.size);
NSNumber *n_offset = @(offset);
NSNumber *n_time = [NSNumber numberWithLongLong:_modifyTime];
NSMutableDictionary *rec = [NSMutableDictionary dictionaryWithObjectsAndKeys:n_size, @"size", n_offset, @"offset", n_time, @"modify_time", _contexts, @"contexts", nil];
NSError *error;
NSData *data = [NSJSONSerialization dataWithJSONObject:rec options:NSJSONWritingPrettyPrinted error:&error];
if (error != nil) {
NSLog(@"up record json error %@ %@", key, error);
return;
}
error = [_recorder set:key data:data];
if (error != nil) {
NSLog(@"up record set error %@ %@", key, error);
}
}
- (void)removeRecord {
if (_recorder == nil) {
return;
}
[_recorder del:self.recorderKey];
}
- (UInt32)recoveryFromRecord {
NSString *key = self.recorderKey;
if (_recorder == nil || key == nil || [key isEqualToString:@""]) {
return 0;
}
NSData *data = [_recorder get:key];
if (data == nil) {
return 0;
}
NSError *error;
NSDictionary *info = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&error];
if (error != nil) {
NSLog(@"recovery error %@ %@", key, error);
[_recorder del:self.key];
return 0;
}
NSNumber *n_offset = info[@"offset"];
NSNumber *n_size = info[@"size"];
NSNumber *time = info[@"modify_time"];
NSArray *contexts = info[@"contexts"];
if (n_offset == nil || n_size == nil || time == nil || contexts == nil) {
return 0;
}
UInt32 offset = [n_offset unsignedIntValue];
UInt32 size = [n_size unsignedIntValue];
if (offset > size || size != self.size) {
return 0;
}
UInt64 t = [time unsignedLongLongValue];
if (t != _modifyTime) {
NSLog(@"modify time changed %llu, %llu", t, _modifyTime);
return 0;
}
_contexts = [[NSMutableArray alloc] initWithArray:contexts copyItems:true];
return offset;
}
- (void)nextTask:(UInt32)offset retriedTimes:(int)retried host:(NSString *)host {
if (self.option.cancellationSignal()) {
self.complete([QNResponseInfo cancel], self.key, nil);
return;
}
if (offset == self.size) {
QNCompleteBlock completionHandler = ^(QNResponseInfo *info, NSDictionary *resp) {
if (info.isOK) {
[self removeRecord];
self.option.progressHandler(self.key, 1.0);
} else if (info.couldRetry && retried < _config.retryMax) {
[self nextTask:offset retriedTimes:retried + 1 host:host];
return;
}
self.complete(info, self.key, resp);
};
[self makeFile:host complete:completionHandler];
return;
}
UInt32 chunkSize = [self calcPutSize:offset];
QNInternalProgressBlock progressBlock = ^(long long totalBytesWritten, long long totalBytesExpectedToWrite) {
float percent = (float)(offset + totalBytesWritten) / (float)self.size;
if (percent > 0.95) {
percent = 0.95;
}
if (percent > _previousPercent) {
_previousPercent = percent;
} else {
percent = _previousPercent;
}
self.option.progressHandler(self.key, percent);
};
QNCompleteBlock completionHandler = ^(QNResponseInfo *info, NSDictionary *resp) {
if (info.error != nil) {
if (info.statusCode == 701) {
[self nextTask:(offset / kQNBlockSize) * kQNBlockSize retriedTimes:0 host:host];
return;
}
if (retried >= _config.retryMax || !info.couldRetry) {
self.complete(info, self.key, resp);
return;
}
NSString *nextHost = host;
if (info.isConnectionBroken || info.needSwitchServer) {
nextHost = [_config.zone upBackup:_token].address;
}
[self nextTask:offset retriedTimes:retried + 1 host:nextHost];
return;
}
if (resp == nil) {
[self nextTask:offset retriedTimes:retried host:host];
return;
}
NSString *ctx = resp[@"ctx"];
NSNumber *crc = resp[@"crc32"];
if (ctx == nil || crc == nil || [crc unsignedLongValue] != _chunkCrc) {
[self nextTask:offset retriedTimes:retried host:host];
return;
}
_contexts[offset / kQNBlockSize] = ctx;
[self record:offset + chunkSize];
[self nextTask:offset + chunkSize retriedTimes:retried host:host];
};
if (offset % kQNBlockSize == 0) {
UInt32 blockSize = [self calcBlockSize:offset];
[self makeBlock:host offset:offset blockSize:blockSize chunkSize:chunkSize progress:progressBlock complete:completionHandler];
return;
}
NSString *context = _contexts[offset / kQNBlockSize];
[self putChunk:host offset:offset size:chunkSize context:context progress:progressBlock complete:completionHandler];
}
- (UInt32)calcPutSize:(UInt32)offset {
UInt32 left = self.size - offset;
return left < _config.chunkSize ? left : _config.chunkSize;
}
- (UInt32)calcBlockSize:(UInt32)offset {
UInt32 left = self.size - offset;
return left < kQNBlockSize ? left : kQNBlockSize;
}
- (void)makeBlock:(NSString *)uphost
offset:(UInt32)offset
blockSize:(UInt32)blockSize
chunkSize:(UInt32)chunkSize
progress:(QNInternalProgressBlock)progressBlock
complete:(QNCompleteBlock)complete {
NSData *data = [self.file read:offset size:chunkSize];
NSString *url = [[NSString alloc] initWithFormat:@"%@/mkblk/%u", uphost, (unsigned int)blockSize];
_chunkCrc = [QNCrc32 data:data];
[self post:url withData:data withCompleteBlock:complete withProgressBlock:progressBlock];
}
- (void)putChunk:(NSString *)uphost
offset:(UInt32)offset
size:(UInt32)size
context:(NSString *)context
progress:(QNInternalProgressBlock)progressBlock
complete:(QNCompleteBlock)complete {
NSData *data = [self.file read:offset size:size];
UInt32 chunkOffset = offset % kQNBlockSize;
NSString *url = [[NSString alloc] initWithFormat:@"%@/bput/%@/%u", uphost, context, (unsigned int)chunkOffset];
_chunkCrc = [QNCrc32 data:data];
[self post:url withData:data withCompleteBlock:complete withProgressBlock:progressBlock];
}
- (void)makeFile:(NSString *)uphost
complete:(QNCompleteBlock)complete {
NSString *mime = [[NSString alloc] initWithFormat:@"/mimeType/%@", [QNUrlSafeBase64 encodeString:self.option.mimeType]];
__block NSString *url = [[NSString alloc] initWithFormat:@"%@/mkfile/%u%@", uphost, (unsigned int)self.size, mime];
if (self.key != nil) {
NSString *keyStr = [[NSString alloc] initWithFormat:@"/key/%@", [QNUrlSafeBase64 encodeString:self.key]];
url = [NSString stringWithFormat:@"%@%@", url, keyStr];
}
[self.option.params enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL *stop) {
url = [NSString stringWithFormat:@"%@/%@/%@", url, key, [QNUrlSafeBase64 encodeString:obj]];
}];
//
NSString *fname = [[NSString alloc] initWithFormat:@"/fname/%@", [QNUrlSafeBase64 encodeString:[self fileBaseName]]];
url = [NSString stringWithFormat:@"%@%@", url, fname];
NSMutableData *postData = [NSMutableData data];
NSString *bodyStr = [self.contexts componentsJoinedByString:@","];
[postData appendData:[bodyStr dataUsingEncoding:NSUTF8StringEncoding]];
[self post:url withData:postData withCompleteBlock:complete withProgressBlock:nil];
}
#pragma mark -
- (NSString *)fileBaseName {
return [[_file path] lastPathComponent];
}
- (void)post:(NSString *)url
withData:(NSData *)data
withCompleteBlock:(QNCompleteBlock)completeBlock
withProgressBlock:(QNInternalProgressBlock)progressBlock {
[_httpManager post:url withData:data withParams:nil withHeaders:_headers withCompleteBlock:completeBlock withProgressBlock:progressBlock withCancelBlock:_option.cancellationSignal withAccess:_access];
}
- (void)run {
@autoreleasepool {
UInt32 offset = [self recoveryFromRecord];
[self nextTask:offset retriedTimes:0 host:[_config.zone up:_token].address];
}
}
@end

View File

@@ -1,23 +0,0 @@
//
// QNUpToken.h
// QiniuSDK
//
// Created by bailong on 15/6/7.
// Copyright (c) 2015年 Qiniu. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface QNUpToken : NSObject
+ (instancetype)parse:(NSString *)token;
@property (copy, nonatomic, readonly) NSString *access;
@property (copy, nonatomic, readonly) NSString *bucket;
@property (copy, nonatomic, readonly) NSString *token;
@property (readonly) BOOL hasReturnUrl;
- (NSString *)index;
@end

View File

@@ -1,73 +0,0 @@
//
// QNUpToken.m
// QiniuSDK
//
// Created by bailong on 15/6/7.
// Copyright (c) 2015 Qiniu. All rights reserved.
//
#import "QNUrlSafeBase64.h"
#import "QNUpToken.h"
@interface QNUpToken ()
- (instancetype)init:(NSDictionary *)policy token:(NSString *)token;
@end
@implementation QNUpToken
- (instancetype)init:(NSDictionary *)policy token:(NSString *)token {
if (self = [super init]) {
_token = token;
_access = [self getAccess];
_bucket = [self getBucket:policy];
_hasReturnUrl = (policy[@"returnUrl"] != nil);
}
return self;
}
- (NSString *)getAccess {
NSRange range = [_token rangeOfString:@":" options:NSCaseInsensitiveSearch];
return [_token substringToIndex:range.location];
}
- (NSString *)getBucket:(NSDictionary *)info {
NSString *scope = [info objectForKey:@"scope"];
if (!scope) {
return @"";
}
NSRange range = [scope rangeOfString:@":"];
if (range.location == NSNotFound) {
return scope;
}
return [scope substringToIndex:range.location];
}
+ (instancetype)parse:(NSString *)token {
if (token == nil) {
return nil;
}
NSArray *array = [token componentsSeparatedByString:@":"];
if (array == nil || array.count != 3) {
return nil;
}
NSData *data = [QNUrlSafeBase64 decodeString:array[2]];
NSError *tmp = nil;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&tmp];
if (tmp != nil || dict[@"scope"] == nil || dict[@"deadline"] == nil) {
return nil;
}
return [[QNUpToken alloc] init:dict token:token];
}
- (NSString *)index {
return [NSString stringWithFormat:@"%@:%@", _access, _bucket];
}
@end

View File

@@ -1,155 +0,0 @@
//
// QNUploader.h
// QiniuSDK
//
// Created by bailong on 14-9-28.
// Copyright (c) 2014年 Qiniu. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "QNRecorderDelegate.h"
@class QNResponseInfo;
@class QNUploadOption;
@class QNConfiguration;
@class ALAsset;
@class PHAsset;
@class PHAssetResource;
/**
* 上传完成后的回调函数
*
* @param info 上下文信息,包括状态码,错误值
* @param key 上传时指定的key原样返回
* @param resp 上传成功会返回文件信息失败为nil; 可以通过此值是否为nil 判断上传结果
*/
typedef void (^QNUpCompletionHandler)(QNResponseInfo *info, NSString *key, NSDictionary *resp);
/**
管理上传的类,可以生成一次,持续使用,不必反复创建。
*/
@interface QNUploadManager : NSObject
/**
* 默认构造方法,没有持久化记录
*
* @return 上传管理类实例
*/
- (instancetype)init;
/**
* 使用一个持久化的记录接口进行记录的构造方法
*
* @param recorder 持久化记录接口实现
*
* @return 上传管理类实例
*/
- (instancetype)initWithRecorder:(id<QNRecorderDelegate>)recorder;
/**
* 使用持久化记录接口以及持久化key生成函数的构造方法默认情况下使用上传存储的key, 如果key为nil或者有特殊字符比如/,建议使用自己的生成函数
*
* @param recorder 持久化记录接口实现
* @param recorderKeyGenerator 持久化记录key生成函数
*
* @return 上传管理类实例
*/
- (instancetype)initWithRecorder:(id<QNRecorderDelegate>)recorder
recorderKeyGenerator:(QNRecorderKeyGenerator)recorderKeyGenerator;
/**
* 使用配置信息生成上传实例
*
* @param config 配置信息
*
* @return 上传管理类实例
*/
- (instancetype)initWithConfiguration:(QNConfiguration *)config;
/**
* 方便使用的单例方法
*
* @param config 配置信息
*
* @return 上传管理类实例
*/
+ (instancetype)sharedInstanceWithConfiguration:(QNConfiguration *)config;
/**
* 直接上传数据
*
* @param data 待上传的数据
* @param key 上传到云存储的key为nil时表示是由七牛生成
* @param token 上传需要的token, 由服务器生成
* @param completionHandler 上传完成后的回调函数
* @param option 上传时传入的可选参数
*/
- (void)putData:(NSData *)data
key:(NSString *)key
token:(NSString *)token
complete:(QNUpCompletionHandler)completionHandler
option:(QNUploadOption *)option;
/**
* 上传文件
*
* @param filePath 文件路径
* @param key 上传到云存储的key为nil时表示是由七牛生成
* @param token 上传需要的token, 由服务器生成
* @param completionHandler 上传完成后的回调函数
* @param option 上传时传入的可选参数
*/
- (void)putFile:(NSString *)filePath
key:(NSString *)key
token:(NSString *)token
complete:(QNUpCompletionHandler)completionHandler
option:(QNUploadOption *)option;
/**
* 上传ALAsset文件
*
* @param alasset ALAsset文件
* @param key 上传到云存储的key为nil时表示是由七牛生成
* @param token 上传需要的token, 由服务器生成
* @param completionHandler 上传完成后的回调函数
* @param option 上传时传入的可选参数
*/
- (void)putALAsset:(ALAsset *)asset
key:(NSString *)key
token:(NSString *)token
complete:(QNUpCompletionHandler)completionHandler
option:(QNUploadOption *)option;
/**
* 上传PHAsset文件(IOS8 andLater)
*
* @param asset PHAsset文件
* @param key 上传到云存储的key为nil时表示是由七牛生成
* @param token 上传需要的token, 由服务器生成
* @param completionHandler 上传完成后的回调函数
* @param option 上传时传入的可选参数
*/
- (void)putPHAsset:(PHAsset *)asset
key:(NSString *)key
token:(NSString *)token
complete:(QNUpCompletionHandler)completionHandler
option:(QNUploadOption *)option;
/**
* 上传PHAssetResource文件(IOS9.1 andLater)
*
* @param asset PHAssetResource文件
* @param key 上传到云存储的key为nil时表示是由七牛生成
* @param token 上传需要的token, 由服务器生成
* @param completionHandler 上传完成后的回调函数
* @param option 上传时传入的可选参数
*/
- (void)putPHAssetResource:(PHAssetResource *)assetResource
key:(NSString *)key
token:(NSString *)token
complete:(QNUpCompletionHandler)completionHandler
option:(QNUploadOption *)option;
@end

View File

@@ -1,321 +0,0 @@
//
// QNUploader.h
// QiniuSDK
//
// Created by bailong on 14-9-28.
// Copyright (c) 2014 Qiniu. All rights reserved.
//
#import <Foundation/Foundation.h>
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#import "QNALAssetFile.h"
#import <AssetsLibrary/AssetsLibrary.h>
#import <MobileCoreServices/MobileCoreServices.h>
#import <UIKit/UIKit.h>
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
#import "QNPHAssetFile.h"
#import <Photos/Photos.h>
#endif
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 90100
#import "QNPHAssetResource.h"
#endif
#else
#import <CoreServices/CoreServices.h>
#endif
#import "QNAsyncRun.h"
#import "QNConfiguration.h"
#import "QNCrc32.h"
#import "QNFile.h"
#import "QNFormUpload.h"
#import "QNResponseInfo.h"
#import "QNResumeUpload.h"
#import "QNSessionManager.h"
#import "QNSystem.h"
#import "QNUpToken.h"
#import "QNUploadManager.h"
#import "QNUploadOption+Private.h"
@interface QNUploadManager ()
@property (nonatomic) id<QNHttpDelegate> httpManager;
@property (nonatomic) QNConfiguration *config;
@end
@implementation QNUploadManager
- (instancetype)init {
return [self initWithConfiguration:nil];
}
- (instancetype)initWithRecorder:(id<QNRecorderDelegate>)recorder {
return [self initWithRecorder:recorder recorderKeyGenerator:nil];
}
- (instancetype)initWithRecorder:(id<QNRecorderDelegate>)recorder
recorderKeyGenerator:(QNRecorderKeyGenerator)recorderKeyGenerator {
QNConfiguration *config = [QNConfiguration build:^(QNConfigurationBuilder *builder) {
builder.recorder = recorder;
builder.recorderKeyGen = recorderKeyGenerator;
}];
return [self initWithConfiguration:config];
}
- (instancetype)initWithConfiguration:(QNConfiguration *)config {
if (self = [super init]) {
if (config == nil) {
config = [QNConfiguration build:^(QNConfigurationBuilder *builder){
}];
}
_config = config;
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090)
_httpManager = [[QNSessionManager alloc] initWithProxy:config.proxy timeout:config.timeoutInterval urlConverter:config.converter dns:config.dns];
#endif
}
return self;
}
+ (instancetype)sharedInstanceWithConfiguration:(QNConfiguration *)config {
static QNUploadManager *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] initWithConfiguration:config];
});
return sharedInstance;
}
+ (BOOL)checkAndNotifyError:(NSString *)key
token:(NSString *)token
input:(NSObject *)input
complete:(QNUpCompletionHandler)completionHandler {
NSString *desc = nil;
if (completionHandler == nil) {
@throw [NSException exceptionWithName:NSInvalidArgumentException
reason:@"no completionHandler"
userInfo:nil];
return YES;
}
if (input == nil) {
desc = @"no input data";
} else if (token == nil || [token isEqualToString:@""]) {
desc = @"no token";
}
if (desc != nil) {
QNAsyncRunInMain(^{
completionHandler([QNResponseInfo responseInfoWithInvalidArgument:desc], key, nil);
});
return YES;
}
return NO;
}
- (void)putData:(NSData *)data
key:(NSString *)key
token:(NSString *)token
complete:(QNUpCompletionHandler)completionHandler
option:(QNUploadOption *)option {
if ([QNUploadManager checkAndNotifyError:key token:token input:data complete:completionHandler]) {
return;
}
QNUpToken *t = [QNUpToken parse:token];
if (t == nil) {
QNAsyncRunInMain(^{
completionHandler([QNResponseInfo responseInfoWithInvalidToken:@"invalid token"], key, nil);
});
return;
}
[_config.zone preQuery:t on:^(int code) {
if (code != 0) {
QNAsyncRunInMain(^{
completionHandler([QNResponseInfo responseInfoWithInvalidToken:@"get zone failed"], key, nil);
});
return;
}
if ([data length] == 0) {
QNAsyncRunInMain(^{
completionHandler([QNResponseInfo responseInfoOfZeroData:nil], key, nil);
});
return;
}
QNUpCompletionHandler complete = ^(QNResponseInfo *info, NSString *key, NSDictionary *resp) {
QNAsyncRunInMain(^{
completionHandler(info, key, resp);
});
};
QNFormUpload *up = [[QNFormUpload alloc]
initWithData:data
withKey:key
withToken:t
withCompletionHandler:complete
withOption:option
withHttpManager:_httpManager
withConfiguration:_config];
QNAsyncRun(^{
[up put];
});
}];
}
- (void)putFileInternal:(id<QNFileDelegate>)file
key:(NSString *)key
token:(NSString *)token
complete:(QNUpCompletionHandler)completionHandler
option:(QNUploadOption *)option {
@autoreleasepool {
QNUpToken *t = [QNUpToken parse:token];
if (t == nil) {
QNAsyncRunInMain(^{
completionHandler([QNResponseInfo responseInfoWithInvalidToken:@"invalid token"], key, nil);
});
return;
}
[_config.zone preQuery:t on:^(int code) {
if (code != 0) {
QNAsyncRunInMain(^{
completionHandler([QNResponseInfo responseInfoWithInvalidToken:@"get zone failed"], key, nil);
});
return;
}
QNUpCompletionHandler complete = ^(QNResponseInfo *info, NSString *key, NSDictionary *resp) {
[file close];
QNAsyncRunInMain(^{
completionHandler(info, key, resp);
});
};
if ([file size] <= _config.putThreshold) {
NSData *data = [file readAll];
[self putData:data key:key token:token complete:complete option:option];
return;
}
NSString *recorderKey = key;
if (_config.recorder != nil && _config.recorderKeyGen != nil) {
recorderKey = _config.recorderKeyGen(key, [file path]);
}
NSLog(@"recorder %@", _config.recorder);
QNResumeUpload *up = [[QNResumeUpload alloc]
initWithFile:file
withKey:key
withToken:t
withCompletionHandler:complete
withOption:option
withRecorder:_config.recorder
withRecorderKey:recorderKey
withHttpManager:_httpManager
withConfiguration:_config];
QNAsyncRun(^{
[up run];
});
}];
}
}
- (void)putFile:(NSString *)filePath
key:(NSString *)key
token:(NSString *)token
complete:(QNUpCompletionHandler)completionHandler
option:(QNUploadOption *)option {
if ([QNUploadManager checkAndNotifyError:key token:token input:filePath complete:completionHandler]) {
return;
}
@autoreleasepool {
NSError *error = nil;
__block QNFile *file = [[QNFile alloc] init:filePath error:&error];
if (error) {
QNAsyncRunInMain(^{
QNResponseInfo *info = [QNResponseInfo responseInfoWithFileError:error];
completionHandler(info, key, nil);
});
return;
}
[self putFileInternal:file key:key token:token complete:completionHandler option:option];
}
}
- (void)putALAsset:(ALAsset *)asset
key:(NSString *)key
token:(NSString *)token
complete:(QNUpCompletionHandler)completionHandler
option:(QNUploadOption *)option {
#if __IPHONE_OS_VERSION_MIN_REQUIRED
if ([QNUploadManager checkAndNotifyError:key token:token input:asset complete:completionHandler]) {
return;
}
@autoreleasepool {
NSError *error = nil;
__block QNALAssetFile *file = [[QNALAssetFile alloc] init:asset error:&error];
if (error) {
QNAsyncRunInMain(^{
QNResponseInfo *info = [QNResponseInfo responseInfoWithFileError:error];
completionHandler(info, key, nil);
});
return;
}
[self putFileInternal:file key:key token:token complete:completionHandler option:option];
}
#endif
}
- (void)putPHAsset:(PHAsset *)asset
key:(NSString *)key
token:(NSString *)token
complete:(QNUpCompletionHandler)completionHandler
option:(QNUploadOption *)option {
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000)
if ([QNUploadManager checkAndNotifyError:key token:token input:asset complete:completionHandler]) {
return;
}
@autoreleasepool {
NSError *error = nil;
__block QNPHAssetFile *file = [[QNPHAssetFile alloc] init:asset error:&error];
if (error) {
QNAsyncRunInMain(^{
QNResponseInfo *info = [QNResponseInfo responseInfoWithFileError:error];
completionHandler(info, key, nil);
});
return;
}
[self putFileInternal:file key:key token:token complete:completionHandler option:option];
}
#endif
}
- (void)putPHAssetResource:(PHAssetResource *)assetResource
key:(NSString *)key
token:(NSString *)token
complete:(QNUpCompletionHandler)completionHandler
option:(QNUploadOption *)option {
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 90100)
if ([QNUploadManager checkAndNotifyError:key token:token input:assetResource complete:completionHandler]) {
return;
}
@autoreleasepool {
NSError *error = nil;
__block QNPHAssetResource *file = [[QNPHAssetResource alloc] init:assetResource error:&error];
if (error) {
QNAsyncRunInMain(^{
QNResponseInfo *info = [QNResponseInfo responseInfoWithFileError:error];
completionHandler(info, key, nil);
});
return;
}
[self putFileInternal:file key:key token:token complete:completionHandler option:option];
}
#endif
}
@end

View File

@@ -1,14 +0,0 @@
//
// QNUploadOption+Private.h
// QiniuSDK
//
// Created by bailong on 14/10/5.
// Copyright (c) 2014年 Qiniu. All rights reserved.
//
#import "QNUploadOption.h"
@interface QNUploadOption (Private)
@property (nonatomic, getter=priv_isCancelled, readonly) BOOL cancelled;
@end

View File

@@ -1,83 +0,0 @@
//
// QNUploadOption.h
// QiniuSDK
//
// Created by bailong on 14/10/4.
// Copyright (c) 2014年 Qiniu. All rights reserved.
//
#import <Foundation/Foundation.h>
/**
* 上传进度回调函数
*
* @param key 上传时指定的存储key
* @param percent 进度百分比
*/
typedef void (^QNUpProgressHandler)(NSString *key, float percent);
/**
* 上传中途取消函数
*
* @return 如果想取消返回True, 否则返回No
*/
typedef BOOL (^QNUpCancellationSignal)(void);
/**
* 可选参数集合此类初始化后sdk上传使用时 不会对此进行改变;如果参数没有变化以及没有使用依赖,可以重复使用。
*/
@interface QNUploadOption : NSObject
/**
* 用于服务器上传回调通知的自定义参数参数的key必须以x: 开头
*/
@property (copy, nonatomic, readonly) NSDictionary *params;
/**
* 指定文件的mime类型
*/
@property (copy, nonatomic, readonly) NSString *mimeType;
/**
* 是否进行crc校验
*/
@property (readonly) BOOL checkCrc;
/**
* 进度回调函数
*/
@property (copy, readonly) QNUpProgressHandler progressHandler;
/**
* 中途取消函数
*/
@property (copy, readonly) QNUpCancellationSignal cancellationSignal;
/**
* 可选参数的初始化方法
*
* @param mimeType mime类型
* @param progress 进度函数
* @param params 自定义服务器回调参数
* @param check 是否进行crc检查
* @param cancellation 中途取消函数
*
* @return 可选参数类实例
*/
- (instancetype)initWithMime:(NSString *)mimeType
progressHandler:(QNUpProgressHandler)progress
params:(NSDictionary *)params
checkCrc:(BOOL)check
cancellationSignal:(QNUpCancellationSignal)cancellation;
- (instancetype)initWithProgessHandler:(QNUpProgressHandler)progress DEPRECATED_ATTRIBUTE;
- (instancetype)initWithProgressHandler:(QNUpProgressHandler)progress;
/**
* 内部使用,默认的参数实例
*
* @return 可选参数类实例
*/
+ (instancetype)defaultOptions;
@end

View File

@@ -1,67 +0,0 @@
//
// QNUploadOption.m
// QiniuSDK
//
// Created by bailong on 14/10/4.
// Copyright (c) 2014 Qiniu. All rights reserved.
//
#import "QNUploadOption+Private.h"
#import "QNUploadManager.h"
static NSString *mime(NSString *mimeType) {
if (mimeType == nil || [mimeType isEqualToString:@""]) {
return @"application/octet-stream";
}
return mimeType;
}
@implementation QNUploadOption
+ (NSDictionary *)filteParam:(NSDictionary *)params {
NSMutableDictionary *ret = [NSMutableDictionary dictionary];
if (params == nil) {
return ret;
}
[params enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL *stop) {
if ([key hasPrefix:@"x:"] && ![obj isEqualToString:@""]) {
ret[key] = obj;
}
}];
return ret;
}
- (instancetype)initWithProgessHandler:(QNUpProgressHandler)progress {
return [self initWithMime:nil progressHandler:progress params:nil checkCrc:NO cancellationSignal:nil];
}
- (instancetype)initWithProgressHandler:(QNUpProgressHandler)progress {
return [self initWithMime:nil progressHandler:progress params:nil checkCrc:NO cancellationSignal:nil];
}
- (instancetype)initWithMime:(NSString *)mimeType
progressHandler:(QNUpProgressHandler)progress
params:(NSDictionary *)params
checkCrc:(BOOL)check
cancellationSignal:(QNUpCancellationSignal)cancel {
if (self = [super init]) {
_mimeType = mime(mimeType);
_progressHandler = progress != nil ? progress : ^(NSString *key, float percent) {
};
_params = [QNUploadOption filteParam:params];
_checkCrc = check;
_cancellationSignal = cancel != nil ? cancel : ^BOOL() {
return NO;
};
}
return self;
}
+ (instancetype)defaultOptions {
return [[QNUploadOption alloc] initWithMime:nil progressHandler:nil params:nil checkCrc:NO cancellationSignal:nil];
}
@end