项目需求:允许用户可以选择多张图片上传到服务器(使用的七牛云).
思路:
- 一开始想的是任务分发,将所有图片遍历放到子线程中上传,这样N张图片会同时上传。但这样有弊端:如果需要精确显示上传进度,逻辑处理会比较复杂,同时因为不是自己的存储服务器,不能自己根据用户选择的图片顺序存储在服务器上(猜测是我们的服务器懒,不想写)。
- 那就采用逐一上传图片的思路。如果某一张上传成功,执行下一张上传任务。某一张上传失败,记录失败图片下标(可选择重复上传,或保存数据),继续下一张上传任务.
代码:GitHub
实现:
@interface FGUploadImageManager ()
/**
上传失败的数组
*/
@property (nonatomic, strong) NSMutableArray *failedIndexs;
/**
上传图片数据
*/
@property (nonatomic, strong) NSArray *upLoadArray;
/**
标识的下标
*/
@property (nonatomic, assign) NSUInteger currentIndex;
/**
某一张图片失败次数
*/
@property (nonatomic, assign) NSInteger onceFailedCount;
@end
//单张图片上传失败最大次数
const static NSInteger kMaxUploadCount = 3;
/**
上传多张图片入口
*/
- (void)upLoadImageWithImageArray:(NSArray *)imageArray
{
[self cleanData];
//初始化数据
self.failedIndexs = [NSMutableArray array];
self.upLoadArray = [NSArray arrayWithArray:imageArray];
[SVProgressHUD showWithStatus:@"正在上传图片..."];
[self upLoadPhotosOnceCompletion:^(NSUInteger index, BOOL isSuccess) {
if (isSuccess) {
//添加上传成功后的动作...(刷新UI等)
NSLog(@"上传第%zd照片",index);
}
else {
[self.failedIndexs addObject:@(index)];
}
} completeBlock:^{
if (self.failedIndexs.count != 0) {
NSMutableString *mutableString = [NSMutableString string];
for (NSNumber *index in self.failedIndexs) {
[mutableString appendFormat:@"第%@张",index];
}
[SVProgressHUD showErrorWithStatus:[NSString stringWithFormat:@"%@上传失败",mutableString]];
}else{
[SVProgressHUD showSuccessWithStatus:@"图片全部上传成功"];
}
[self cleanData];
}];
}
/**
* 递归上传照片
*/
- (void)upLoadPhotosOnceCompletion:(void(^)(NSUInteger index,BOOL isSuccess))onceCompletion completeBlock:(void(^)(void))completeBlock{
//根据下标,从数组中取出图片,转为二进制数据
NSData * data = UIImagePNGRepresentation(self.upLoadArray[self.currentIndex]);
//发起网络请求
[[FGUploadHTTPRequest shareUpload] uploadPhotoAlbum:data uploadProgress:^(float percent) {
//显示上传的进度
[SVProgressHUD showProgress:(CGFloat)(self.currentIndex + percent) / self.upLoadArray.count status:@"正在上传中"];
} completeBlock:^(BOOL isSuccess) {
//如果上传失败,并且没有超过最大上传次数,重新上传
if (!isSuccess) {
self.onceFailedCount++;
if (self.onceFailedCount < kMaxUploadCount) {
[self upLoadPhotosOnceCompletion:onceCompletion completeBlock:completeBlock];
return;
}
}
//清空失败次数
self.onceFailedCount = 0;
//记录新的下标index++
self.currentIndex++;
//回调一次的结果
if (onceCompletion) onceCompletion(self.currentIndex,isSuccess);
//判断是否上传完毕
if (self.currentIndex == self.upLoadArray.count) {
//如果是已经上传完了,结束
self.currentIndex = 0 ;
if (completeBlock) completeBlock();
}
else
{
//如果还没上传完成,继续下一次上传
[self upLoadPhotosOnceCompletion:onceCompletion completeBlock:completeBlock];
}
}];
}
1.当某张上传失败,会尝试上传kMaxUploadCount次,如果仍然失败,那就放弃本张图片,继续上传下一张图片。
2.全局的下标标识,在完成后一定要清零,不然在同个界面再次操作的时候,标示还保留的上次的最大值,再次调用函数,会抛出异常.数组越界.
3.如果要自己修改需求.一定要写好边界条件.递归在适当的时候返回,不然就会成死循环了.
保留数据持续上传的做法:
如果存在未上传成功的图片,我们可以将图片扔给一个始终存在的单例或者考虑将图片数据写入数据库中,这样保证了图片数据可以一直存在。我们要做的就是讲这份数据一直不停的上传给服务器。
用户多张图片感觉也是这样,应为这几张图片是用户在自己的相册精挑细选出来的,用户想的就是要这图片。不应该就因为一些原因丢失用户的数据。当然这是产品考虑的一些内容。