解决了语音上传到问题,接下来要解决下载播放问题
This commit is contained in:
@@ -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);
|
||||
|
||||
// 将录音文件URL传递给JS接口
|
||||
if (self->_bridge) {
|
||||
[self->_bridge callHandler:@"recordSuccess" data:@{
|
||||
@"fileUrl": fileUrl,
|
||||
@"fileName": uniqueFileName,
|
||||
@"fileKey": key
|
||||
}];
|
||||
}
|
||||
}
|
||||
});
|
||||
}];
|
||||
|
||||
// 原有的上传流程代码注释掉或删除
|
||||
|
||||
} else {
|
||||
NSLog(@"AMR文件无效或为空");
|
||||
}
|
||||
@@ -2476,3 +2531,5 @@
|
||||
}}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
25
msext/Class/Utils/QiniuConfig.h
Normal file
25
msext/Class/Utils/QiniuConfig.h
Normal 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 */
|
||||
22
msext/Class/Utils/QiniuConfig.m
Normal file
22
msext/Class/Utils/QiniuConfig.m
Normal 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 = @"";
|
||||
76
msext/Class/Utils/QiniuManager.h
Normal file
76
msext/Class/Utils/QiniuManager.h
Normal 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
|
||||
256
msext/Class/Utils/QiniuManager.m
Normal file
256
msext/Class/Utils/QiniuManager.m
Normal 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];
|
||||
|
||||
// 指定上传的目标资源空间(确保完整格式为bucketName或bucketName: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;
|
||||
}
|
||||
|
||||
// Base64编码JSON数据
|
||||
NSString *encodedPolicy = [self urlsafeBase64EncodeData:jsonData];
|
||||
|
||||
// 使用HMAC-SHA1算法进行签名(注意签名的内容只是encodedPolicy)
|
||||
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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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] 不能用在大于 200M的文件上,改用filehandle
|
||||
// 参见 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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";
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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) {
|
||||
// 出现错误时,如果信息是非UTF8编码会失败,返回nil
|
||||
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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user