3.上传
上传可以采用同步和异步执行。这里不太建议通过for遍历来开太多的线程上传,开线程是耗内存的。这边我是通过同步的方式。也就是采用递归,一片文件上传完毕后再上传下一片文件,如果失败,再次上传。有一点需要强调,最后一片的大小一般都比会小于预设的最小分割值。另外,如果分的片段大小大于文件的总大小也可能会出问题,客户端和服务器沟通好规则处理即可。
关于上传进度,可以粗略计算。也可使用NSURLSessionDataTask的countOfBytesSent实时监控。其实NSURLSessionTask在iOS11以后还提供了progress属性。附上核心代码提供参考。
首次调用上传接口
#pragma mark- first upload 断点
// 上传初始化
- (void)uploadData:(NSData *)data withModel:(YJTDocUploadModel *)model {
// 计算片数
NSInteger count = data.length / (kSuperUploadBlockSize);
NSInteger blockCount = data.length % (kSuperUploadBlockSize) == 0 ? count : count + 1;
// 给model赋值
model.filePath = [self writeToCacheVideo:data appendNameString:model.lastPathComponent];
model.totalCount = blockCount;
model.totalSize = data.length;
model.uploadedCount = 0;
model.isRunning = YES;
// 上传所需参数
NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
parameters[@"sequenceNo"] = @0;
parameters[@"blockSize"] = @(kSuperUploadBlockSize);
parameters[@"totFileSize"] = @(data.length);
parameters[@"suffix"] = model.filePath.pathExtension;
parameters[@"token"] = @"";
NSString *requestUrl = @"上传接口";
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
NSURLSessionDataTask *dataTask = [manager POST:requestUrl parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
[formData appendPartWithFileData:[NSData data] name:@"block" fileName:model.filePath.lastPathComponent mimeType:@"application/octet-stream"];
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) {
NSDictionary *dataDict = responseObject[kRet_success_data_key];
model.upToken = dataDict[@"upToken"];
NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:model.filePath];
if (handle == nil) { return; }
[self continueUploadWithModel:model];
[self addUploadModel:model];
[[VMProgressHUD sharedInstance] showTipTextOnly:@"正在后台上传" dealy:2];
} failure:^(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error) {
[[VMProgressHUD sharedInstance] showTipTextOnly:error.localizedDescription dealy:1];
}];
model.dataTask = dataTask;
}
核心代码
#pragma mark- continue upload
- (void)continueUploadWithModel:(YJTDocUploadModel *)model {
if (!model.isRunning) {
return;
}
__block NSInteger i = model.uploadedCount;
NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
parameters[@"blockSize"] = @(kSuperUploadBlockSize);
parameters[@"totFileSize"] = @(model.totalSize);
parameters[@"suffix"] = model.filePath.pathExtension;
parameters[@"token"] = @"";
parameters[@"upToken"] = model.upToken;
parameters[@"crc"] = @"";
parameters[@"sequenceNo"] = @(i + 1);
NSString *requestUrl = [[Api getRootUrl] stringByAppendingString:@"上传接口"];
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
NSURLSessionDataTask *dataTask = [manager POST:requestUrl parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:model.filePath];
[handle seekToFileOffset:kSuperUploadBlockSize * i];
NSData *blockData = [handle readDataOfLength:kSuperUploadBlockSize];
[formData appendPartWithFileData:blockData name:@"block" fileName:model.filePath.lastPathComponent mimeType:@"application/octet-stream"];
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) {
i ++;
model.uploadedCount = i;
NSDictionary *dataDict = responseObject[kRet_success_data_key];
NSString *fileUrl = dataDict[@"fileUrl"];
if ([fileUrl isKindOfClass:[NSString class]]) {
[model.parameters setValue:fileUrl forKey:@"url"];
// 最后所有片段上传完毕,服务器返回文件url,执行后续操作
[self saveRequest:model];
}else {
if (i < model.totalCount) {
[self continueUploadWithModel:model];
}
}
} failure:^(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error) {
// 上传失败重试
[self continueUploadWithModel:model];
}];
model.dataTask = dataTask;
}










