// // UploadFile.m // xplan-ios // // Created by GreenLand on 2022/2/23. // #import #import "UploadFile.h" #import #import "Api+Mine.h" #import "UploadFileModel.h" static UploadFile* manager; @interface UploadFile() // 一个脚手架实例 @property (nonatomic) QCloudCredentailFenceQueue* credentialFenceQueue; @property(nonatomic,strong) UploadFileModel *fileModel; // MARK: 批量下载部分, 后续要新建一个 object 来承载业务 @property (nonatomic, strong) AFHTTPSessionManager *manager; @property (nonatomic, strong) NSMutableArray *tasks; @end @implementation UploadFile + (instancetype)share { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ manager = [[UploadFile alloc] init]; }); return manager; } -(void)initQCloud{ @kWeakify(self); [Api getQCloudInfo:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) { @kStrongify(self); if (code == 200){ UploadFileModel *fileModel = [UploadFileModel modelWithDictionary:data.data]; self.fileModel = fileModel; QCloudServiceConfiguration* configuration = [QCloudServiceConfiguration new]; configuration.appID = fileModel.appId; QCloudCOSXMLEndPoint* endpoint = [[QCloudCOSXMLEndPoint alloc] init]; endpoint.regionName = fileModel.region; // 使用 HTTPS endpoint.useHTTPS = YES; configuration.endpoint = endpoint; // 密钥提供者为自己 configuration.signatureProvider = self; // 初始化 COS 服务示例 [QCloudCOSXMLService registerDefaultCOSXMLWithConfiguration:configuration]; [QCloudCOSTransferMangerService registerDefaultCOSTransferMangerWithConfiguration: configuration]; // 全球加速! https://www.tencentcloud.com/zh/document/product/436/38113 if (fileModel.accelerate == 1) { endpoint.suffix = @"cos.accelerate.myqcloud.com"; } self.credentialFenceQueue = [QCloudCredentailFenceQueue new]; self.credentialFenceQueue.delegate = self; } }]; } #pragma mark- QCloudSignatureProvider - (void) signatureWithFields:(QCloudSignatureFields*)fileds request:(QCloudBizHTTPRequest*)request urlRequest:(NSMutableURLRequest*)urlRequst compelete:(QCloudHTTPAuthentationContinueBlock)continueBlock { [Api getQCloudInfo:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) { if (code == 200){ UploadFileModel *fileModel = [UploadFileModel modelWithDictionary:data.data]; QCloudCredential* credential = [QCloudCredential new]; // 临时密钥 SecretId credential.secretID = fileModel.secretId; // 临时密钥 SecretKey credential.secretKey = fileModel.secretKey; // 临时密钥 Token credential.token = fileModel.sessionToken; /** 强烈建议返回服务器时间作为签名的开始时间, 用来避免由于用户手机本地时间偏差过大导致的签名不正确(参数startTime和expiredTime单位为秒) */ credential.startDate = [NSDate dateWithTimeIntervalSince1970:fileModel.startTime]; // 单位是秒 credential.expirationDate = [NSDate dateWithTimeIntervalSince1970:fileModel.expireTime];// 单位是秒 QCloudAuthentationV5Creator* creator = [[QCloudAuthentationV5Creator alloc] initWithCredential:credential]; // 注意 这里不要对urlRequst 进行copy以及mutableCopy操作 QCloudSignature *signature = [creator signatureForData:urlRequst]; continueBlock(signature, nil); } } ]; } #pragma mark - QCloudCredentailFenceQueueDelegate - (void) fenceQueue:(QCloudCredentailFenceQueue * )queue requestCreatorWithContinue:(QCloudCredentailFenceQueueContinue)continueBlock { //这里同步从◊后台服务器获取临时密钥,强烈建议将获取临时密钥的逻辑放在这里,最大程度上保证密钥的可用性 //... [Api getQCloudInfo:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) { if (code == 200){ UploadFileModel *fileModel = [UploadFileModel modelWithDictionary:data.data]; QCloudCredential* credential = [QCloudCredential new]; // 临时密钥 SecretId credential.secretID = fileModel.secretId; // 临时密钥 SecretKey credential.secretKey = fileModel.secretKey; // 临时密钥 Token credential.token = fileModel.sessionToken; /** 强烈建议返回服务器时间作为签名的开始时间, 用来避免由于用户手机本地时间偏差过大导致的签名不正确(参数startTime和expiredTime单位为秒) */ credential.startDate = [NSDate dateWithTimeIntervalSince1970:fileModel.startTime]; // 单位是秒 credential.expirationDate = [NSDate dateWithTimeIntervalSince1970:fileModel.expireTime];// 单位是秒 QCloudAuthentationV5Creator* creator = [[QCloudAuthentationV5Creator alloc] initWithCredential:credential]; continueBlock(creator, nil); } }]; } /// 上传一个文件 /// @param filePath 文件地址 /// @param fileName 文件的名字 /// @param success 成功 /// @param failure 失败 - (void)QCloudUploadFile:(NSString *)filePath named:(NSString *)fileName success:(void (^)(NSString *key, NSDictionary *resp))success failure:(void (^)(NSNumber *resCode, NSString *message))failure { QCloudCOSXMLUploadObjectRequest* put = [QCloudCOSXMLUploadObjectRequest new]; // 本地文件路径 NSURL* url = [NSURL fileURLWithPath:filePath]; // 存储桶名称,由BucketName-Appid 组成,可以在COS控制台查看 https://console.cloud.tencent.com/cos5/bucket put.bucket = self.fileModel.bucket; // 对象键,是对象在 COS 上的完整路径,如果带目录的话,格式为 "video/xxx/movie.mp4" put.object = fileName; //需要上传的对象内容。可以传入NSData*或者NSURL*类型的变量 put.body = url; //监听上传进度 [put setSendProcessBlock:^(int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) { // bytesSent 本次要发送的字节数(一个大文件可能要分多次发送) // totalBytesSent 已发送的字节数 // totalBytesExpectedToSend 本次上传要发送的总字节数(即一个文件大小) }]; //监听上传结果 [put setFinishBlock:^(id outputObject, NSError *error) { //可以从 outputObject 中获取 response 中 etag 或者自定义头部等信息 if (error) { failure(@(error.code),error.localizedDescription); return; } QCloudUploadObjectResult * result = (QCloudUploadObjectResult *)outputObject; NSArray *urlList = [result.location componentsSeparatedByString:@".com/"]; if (urlList.count == 2){ NSString *url = [NSString stringWithFormat:@"%@/%@",self.fileModel.customDomain,urlList[1]]; success(url,nil); return; } success(result.location,nil); }]; [[QCloudCOSTransferMangerService defaultCOSTransferManager] UploadObject:put]; } /// 上传一个Image /// @param image 图片 /// @param imageName 图片的名字 /// @param success 成功 /// @param failure 失败 - (void)QCloudUploadImage:(NSData *)data named:(NSString *)name success:(void (^)(NSString *key, NSDictionary *resp))success failure:(void (^)(NSNumber *resCode, NSString *message))failure{ QCloudCOSXMLUploadObjectRequest* put = [QCloudCOSXMLUploadObjectRequest new]; put.bucket = self.fileModel.bucket; // 对象键,是对象在 COS 上的完整路径,如果带目录的话,格式为 "video/xxx/movie.mp4" put.object = name; //需要上传的对象内容。可以传入NSData*或者NSURL*类型的变量 put.body = data; //监听上传进度 [put setSendProcessBlock:^(int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) { // bytesSent 本次要发送的字节数(一个大文件可能要分多次发送) // totalBytesSent 已发送的字节数 // totalBytesExpectedToSend 本次上传要发送的总字节数(即一个文件大小) }]; //监听上传结果 [put setFinishBlock:^(id outputObject, NSError *error) { if (error) { failure(@(error.code),error.localizedDescription); return; } QCloudUploadObjectResult * result = (QCloudUploadObjectResult *)outputObject; NSArray *urlList = [result.location componentsSeparatedByString:@".com/"]; if (urlList.count == 2){ NSString *url = [NSString stringWithFormat:@"%@/%@",self.fileModel.customDomain,urlList[1]]; success(url,nil); return; } success(result.location,nil); }]; [[QCloudCOSTransferMangerService defaultCOSTransferManager] UploadObject:put]; } -(void)downloadAnimationFileName:(NSString *)fileName localPath:(NSString *)localPath completion:(void (^) (BOOL isSuccess, NSString *editAudioPath))completion{ QCloudCOSXMLDownloadObjectRequest * request = [QCloudCOSXMLDownloadObjectRequest new]; // 存储桶名称,由BucketName-Appid 组成,可以在COS控制台查看 https://console.cloud.tencent.com/cos5/bucket request.bucket = self.fileModel.bucket; NSArray *urlList = [fileName componentsSeparatedByString:@".com/"]; // 对象键,是对象在 COS 上的完整路径,如果带目录的话,格式为 "video/xxx/movie.mp4" request.object = [urlList xpSafeObjectAtIndex:1]; // 设置下载的路径 URL,如果设置了,文件将会被下载到指定路径中 request.downloadingURL = [NSURL fileURLWithPath:localPath]; // 监听下载结果 [request setFinishBlock:^(id outputObject, NSError *error) { // outputObject 包含所有的响应 http 头部 NSDictionary* info = (NSDictionary *) outputObject; // NSLog(@"%@",info); }]; // 监听下载进度 [request setDownProcessBlock:^(int64_t bytesDownload, int64_t totalBytesDownload, int64_t totalBytesExpectedToDownload) { // bytesDownload 新增字节数 // totalBytesDownload 本次下载接收的总字节数 // totalBytesExpectedToDownload 本次下载的目标字节数 }]; [[QCloudCOSTransferMangerService defaultCOSTransferManager] DownloadObject:request]; } +(void)downloadAudioWithFileName:(NSString *)fileName musicUrl:(NSString *)musicUrl mainFileName:(NSString *)mainFileName completion:(void (^) (BOOL isSuccess, NSString *editAudioPath))completion { AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; NSURL *url = [NSURL URLWithString:[musicUrl stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]]; NSURLRequest *request = [NSURLRequest requestWithURL :url]; NSURLSessionDownloadTask *download = [manager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) { } destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) { NSString *filePath = [[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) xpSafeObjectAtIndex:0] stringByAppendingPathComponent:mainFileName] stringByAppendingPathComponent:fileName]; return [NSURL fileURLWithPath:filePath]; } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) { if (!error) { completion(YES, filePath.path); } else { completion(NO, nil); } }]; [download resume]; } // 批量下载 - (void)startBatchDownloadWithURLs:(NSArray *)URLs { if (_manager == nil) { _manager = [AFHTTPSessionManager manager]; _manager.operationQueue.maxConcurrentOperationCount = 10; _tasks = [NSMutableArray array]; } if (URLs.count == 0) { return; } [self createCacheLocalPath]; for (NSString *urlStr in URLs) { // 缓存本地目标位置 NSURL *destinationURL = [self localPath:urlStr]; // 检查文件是否已经存在 if ([[NSFileManager defaultManager] fileExistsAtPath:[destinationURL path]]) { continue; } NSURL *url = [NSURL URLWithString:urlStr]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; @kWeakify(self); __block NSURLSessionDownloadTask *downloadTask; downloadTask = [self.manager downloadTaskWithRequest:request progress:nil destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) { return destinationURL; } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) { @kStrongify(self); if ([self.tasks containsObject:downloadTask]) { [self.tasks removeObject:downloadTask]; } }]; [self.tasks addObject:downloadTask]; } [self resumeBatchDownload]; } - (void)pauseBatchDownload { for (NSURLSessionDownloadTask *task in self.tasks) { if (task.state == NSURLSessionTaskStateRunning) { [task suspend]; } } } - (void)resumeBatchDownload { for (NSURLSessionDownloadTask *task in self.tasks) { if (task.state == NSURLSessionTaskStateSuspended) { [task resume]; } } } - (void)createCacheLocalPath { NSError *error = nil; NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *filePath = [self cacheFilePath]; if ([fileManager fileExistsAtPath:filePath] == NO) { NSFileManager *fileMgr = [[NSFileManager alloc] init]; BOOL success = [fileMgr createDirectoryAtPath:filePath withIntermediateDirectories:YES attributes:nil error:&error]; if (success) { // NSLog(@"Folder created successfully."); } else { // NSLog(@"Could not create folder: %@", [error localizedDescription]); } } } - (NSURL *)localPath:(NSString *)encodingUrl { NSString *fileName = [[encodingUrl componentsSeparatedByString:@"/"] lastObject]; NSString *filePath = [self cacheFilePath]; NSString *cacheLocalPath = [filePath stringByAppendingPathComponent:fileName]; return [NSURL fileURLWithPath:cacheLocalPath]; } - (NSString *)cacheFilePath { return [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) xpSafeObjectAtIndex:0] stringByAppendingPathComponent:@"GiftDynamicEffectList"]; } - (void)download:(NSString *)targetURLString path:(NSString *)localFilePath complete:(void(^)(void))complete failure:(void(^)(void))failure { AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; NSURL *pathURL = [NSURL fileURLWithPath:localFilePath]; if ([[NSFileManager defaultManager] fileExistsAtPath:pathURL.path]) { return; } NSURL *url = [NSURL URLWithString:targetURLString]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; @kWeakify(self); NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) { return pathURL; } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) { if (error) { if (failure) { failure(); } } else { if (complete) { complete(); } } }]; [downloadTask resume]; } @end