前言
项目中有一处功能是从相册中选取图片,然后上传至七牛服务器。这儿是以前别人做的,上次功能有小改动,发现这儿的逻辑有些复杂,但当时没时间就没仔细研究。今天有时间捋了一下这里的代码逻辑,捋一遍自然是有收获的,不仅梳理了逻辑,也发现了点问题和需要改进的地方。
功能逻辑
该功能是这样的:首先,这是一个表单界面,表单最后一项需要上传身份证的正反面照片,即需要上传两张图片。然后该界面下面有个“提交”按钮,用于提交该表单界面的数据给服务器。
但请注意,在这里选中的两张图片提交给公司服务器时,是提交什么?应该是提交该图片的访问路径url。也就说在提交表单之前我们得拥有这两张所选图片的访问路径url。但这俩所选图片均是本地相册图片,哪里来的url呢?其实,我们需要将图片上传至七牛服务器,然后就能搞到访问该图片对应的访问路径url。公司的服务器关于该图片的信息只有其url,并无图片本身。客户端若要显示该图片,均是以该url访问所得。
简而言之,就是说公司服务器如果自己来维护图片的存储很麻烦且成本高,而七牛则是专门干这个的,成本低。那就把图片交给七牛服务器来存储,而我们自己服务器则值需要存储这个图片在七牛的访问路径就可以了。
这里的具体逻辑如下图所示:
首先,我们在APP上从相册选取了图片后,首先进行压缩,然后将
UIImage
转换为NSData
,再将此图片以NSData
的形式存入本地文件,等待将其上传至七牛服务器。但是要将图片上传至七牛服务器,我们得同时携带token
和key
作为凭证。凭证从哪里来?凭证从我们的服务器来,APP请求公司服务器获取凭证,然后服务器返回给APP凭证。此时,APP就可以携带着凭证往七牛服务器上传本地图片文件了。上传成功后,七牛响应成功信息,其中包含一个
key
字段,表示该图片的唯一标识。别忘了,我们的目的不仅仅是将APP本地图片上传至七牛,我们还得获得访问已然存入七牛的图片的路径。不然上传还有什么意义,上传完后丧失连接了方式,像失踪在汪洋宇宙。APP以该
key
为参数,向公司服务器请求该key
对应图片的url
。然后服务器返回该图片对应的url
,那我们以后显示该图片就是访问该url
,提交该图片就是提交该url
。(疑问:公司服务器是怎么获得该key
对应图片的url
的?)
代码
我们把它的逻辑捋清楚了,看起代码来也就轻松愉快了很多。
#import "PickPhotoDemoViewController.h"
#import "YWImagePickView.h"
#import "MLSelectPhotoPickerViewController.h"
#import "MLSelectPhotoAssets.h"
#import "PhotoModel.h"
#import "QNUploadManager.h"
#import "YWAFNetManager.h"
#define kImageFilePath(name) [NSTemporaryDirectory() stringByAppendingPathComponent:name] // 图片路径
#define Photo_W 80.f
@interface PickPhotoDemoViewController ()
@property (nonatomic, strong) NSMutableArray *photoList;
@property (nonatomic, strong) YWImagePickView *imgPickView;
@end
@implementation PickPhotoDemoViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.contentView addSubview:self.imgPickView];
}
#pragma mark - init
- (NSMutableArray *)photoList
{
if (_photoList == nil) {
_photoList = [NSMutableArray array];
}
return _photoList;
}
- (YWImagePickView *)imgPickView{
if(!_imgPickView){
_imgPickView = [[YWImagePickView alloc] initWithFrame:CGRectMake(20.f, 100.f, PDWidth_mainScreen-40.f, Photo_W)];
_imgPickView.imgLength = Photo_W;
_imgPickView.eachRowCount = 2;
__weak PickPhotoDemoViewController *weakSelf = self;
_imgPickView.addPhotoBtnBlock = ^(){
[weakSelf openPhotoAlbum];
};
__weak YWImagePickView *weakImgPickV = _imgPickView;
_imgPickView.deletePhotoBtnBlock = ^(NSInteger index){
[weakSelf.photoList removeObjectAtIndex:index];
[weakImgPickV refreshContent:weakSelf.photoList];
};
}
return _imgPickView;
}
#pragma mark - function
// 打开相册,选取图片
- (void)openPhotoAlbum
{
MLSelectPhotoPickerViewController *pickerVc = [[MLSelectPhotoPickerViewController alloc] init];
// 默认显示相册里面的内容SavePhotos
pickerVc.status = PickerViewShowStatusCameraRoll;
pickerVc.minCount = 2 - self.photoList.count;
[pickerVc showPickerVc:self];
__weak typeof(self) weakSelf = self;
pickerVc.callBack = ^(NSArray *assets){
for(int i=0; i<assets.count; i++)
{
MLSelectPhotoAssets *asset = assets[i];
UIImage *newImage = [[self class] imageWith:asset.originImage scaledToSize:CGSizeMake(Photo_W, Photo_W)]; // 图片压缩
NSData *imageData = UIImageJPEGRepresentation(newImage, 1);// UIImage -> NSData
NSString *fileName = [NSString stringWithFormat:@"%d.png",arc4random()];
[imageData writeToFile:kImageFilePath(fileName) atomically:YES]; // 将NSData写入本地文件
[weakSelf requestUploadTokenFileName:fileName]; // 请求上传图片所需的token
}
};
}
// 申请图片上传的token
- (void)requestUploadTokenFileName:(NSString *)fileName
{
NSDictionary * parameters=[[NSDictionary alloc] initWithObjectsAndKeys:
@(1), @"count",
nil];
__weak typeof(self) weakSelf = self;
NSString *url = [NSString stringWithFormat:@"%@/cbs/%@/upload/token",HTTPSURLEVER,Interface_Version];
url =[url stringByReplacingOccurrencesOfString:@"http" withString:@"https"];
YWAFNetManager *manager = [YWAFNetManager manager];
[manager GET:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSDictionary *paramDict = DealWithJSONValue(responseObject[@"b"][0]);
[weakSelf uploadPhotoFile:paramDict fileName:fileName]; // 拿到了token,上传图片数据
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[[CustomTips shareInstance] showAutomaticTipsMessage:@"图片token获取失败,请重新选择图片" compelte:nil];
}];
}
//上传图片数据到七牛
- (void)uploadPhotoFile:(NSDictionary *)params fileName:(NSString *)fileName
{
QNUploadManager *uploader = [[QNUploadManager alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *token = params[@"uploadToken"];
NSString *key = params[@"key"];
__weak typeof(self) weakSelf = self;
[uploader putFile:kImageFilePath(fileName) key:key token:token complete:^(QNResponseInfo *info, NSString *key, NSDictionary *resp) {
// 把key赋值给photoModel的photoKey
PhotoModel *photoModel = [PhotoModel new];
photoModel.photoKey = key;
[self.photoList addObject:photoModel];
[weakSelf getPhotoUrlWithKey:resp[@"key"]]; // 请求公司服务器,获得该图片对应的url
} option:nil];
});
}
// 以key为参数获取图片的url
- (void)getPhotoUrlWithKey:(NSString *)key
{
NSDictionary * parameters=[[NSDictionary alloc] initWithObjectsAndKeys:
key, @"token",
nil];
NSString *url = [NSString stringWithFormat:@"%@/cbs/%@/upload/url",HTTPSURLEVER,Interface_Version];
url =[url stringByReplacingOccurrencesOfString:@"http" withString:@"https"];
// __weak typeof(self) weakSelf = self;
YWAFNetManager *manager = [YWAFNetManager manager];
[manager GET:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
if (!responseObject[@"b"] || [responseObject[@"b"] isEqual:[NSNull null]]) {
return;
}
NSString *photoUrl =[NSString stringWithFormat:@"%@",responseObject[@"b"][@"url"]];
// 把url赋值给PhotoModel的photoUrl
PhotoModel *tempPhoto = [self.photoList lastObject];
tempPhoto.photoUrl = photoUrl;
[_imgPickView refreshContent:self.photoList];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[self.photoList removeAllObjects];
[[CustomTips shareInstance] showAutomaticTipsMessage:@"图片上传失败,请重新选择" compelte:nil];
}];
}
//压缩图片
+ (UIImage*)imageWith:(UIImage*)originImage scaledToSize:(CGSize)newSize
{
UIGraphicsBeginImageContext(newSize);
[originImage drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
@end
主要代码放在了百度网盘:APP上传图片至七牛
问题改进
当前的执行逻辑是:用户在APP相册选取了图片后,立马去请求凭证,然后携带着凭证将图片上传至七牛服务器,然后再请求该图片对应的访问路径。当拿到自己服务器返回的图片访问路径后,才去刷新界面,才在_imgPickView
上显示出所选取的几张图片的缩略图。而且,显示图片的方式是加载该图片已获得的url
。然后,当用户点击“提交”按钮时,将表单提交给服务器,提交的是图片的url
。
这样不合理。其一是用户是在本地选取的图片且已存入了本地文件,为什么在界面显示还要加载url
,万一网络不好,用户在相册选取个照片,竟然还要加载半天才显示出图片?!其二是只要选取了照片就立马上传,这是有问题的,会造成不必要的上传开销。比如,用户选了图片后又删除重新选了图片,这样被删除的那个图片其实也已经上传至七牛了。
所以,** 正确的逻辑应当是: 用户在相册选取了图片后,应该立马加载本地的图片刷新界面,先仅仅让所选取的图片显示出来。然后直到用户点击了“提交”按钮后,再去执行“获取凭证,上传图片,获取图片访问路径”这套逻辑代码。等这套都执行完了后,此时客户端拿到了所选取图片的访问路径,然后再把表单提交给服务器。**
代码基本上不变,只是调用顺序的改变。所以就不写代码了。
结尾
深圳这几天变凉了,天凉注意加衣。