我正在写一个照片上传队列管理器。
这是我的流程。用户可以拍照或从相机胶卷中选择一张。 图像以及一些其他字符串数据被放入队列中。 队列只是一个包含图像路径字符串和其他字符串数据的数组。 “排队”图像将保存到应用程序的文档目录中,以便以后检索。
每隔一秒检查队列以查看是否需要上传任何图像,然后逐个上传。
上传时,服务器以success
字符串响应,如果success
,则从列表队列中删除图片,删除临时图像,程序移至下一个排队图像
如果我独立调用uploadImage:
方法并将图像和字符串数据传递给它,图像上传就可以了。但是当从后台线程调用时,即使服务器立即响应成功,图像也永远不会真正上传。我觉得既然服务器立即成功响应,那么图像只是部分上传。
processImage:
是从后台调用uploadImage:
的方法。
我真的可以使用专家的眼睛来看看可能导致应用在继续播放之前没有完全等待照片上传的原因。
@interface ImageUploader ()
@property (nonatomic, strong) NSMutableArray * arrPhotoQueue;
@property (nonatomic, strong) NSTimer* timerUpload;
@end
@implementation ImageUploader {
NSUserDefaults * defaults; //Just were we are currently storing a list of images.
NSArray *paths;
NSString *documentsDirectory;
}
#pragma mark Public Methods
- (void) startPhotoUploadCheck {
if (!_timerUpload) {
if(![_timerUpload isValid]){
_timerUpload = [NSTimer scheduledTimerWithTimeInterval:_timerCheckInterval target:self selector:@selector(checkForPhotosToUpload) userInfo:nil repeats:YES];
}
}
}
- (void) addImageToQueue:(UIImage*)image withCallKey:(NSString*) callKey {
NSData *imageData = UIImagePNGRepresentation(image);
NSString *fileName = [NSString stringWithFormat:@"%@.png",[self randomStringWithLength:8]];
NSString *imagePath =[documentsDirectory stringByAppendingPathComponent:fileName];
NSMutableDictionary * data = [[NSMutableDictionary alloc] init];
[data setValue:fileName forKey:@"imagePath"];
[data setValue:callKey forKey:@"callKey"];
[_arrPhotoQueue addObject:data];
if (![imageData writeToFile:imagePath atomically:NO]) {
NSLog(@"Failed to cache image data to disk");
} else {
NSLog(@"Image %@", fileName);
[defaults setObject:_arrPhotoQueue forKey:@"PendingPhotos"];
[defaults synchronize];
}
[self totalImagesInQueue];
}
- (void) checkForPhotosToUpload {
//this does some logic to just see if there is a connection to the internet and that there are photos in queue then it calls
[self proceedWithUpload];
}
#pragma mark Upload Image
- (int) removePhotoFromMemory:(NSString *) callKeyOfUploadedPhoto
{
int nIndexOfUploadedPhoto = -1;
for (int i = 0; i < [_arrPhotoQueue count]; i++) {
NSString * callKey = [_arrPhotoQueue[i] objectForKey:@"callKey"];
if ([callKey isEqualToString:callKeyOfUploadedPhoto]) {
//remove uploaded photo
NSString *imagePath = [documentsDirectory stringByAppendingPathComponent:[_arrPhotoQueue[i] objectForKey:@"imagePath"]];
NSLog(@"Remove image from queue: %@", [_arrPhotoQueue[i] objectForKey:@"imagePath"]);
[[NSFileManager defaultManager] removeItemAtPath:imagePath error:nil];
nIndexOfUploadedPhoto = i;
break;
}
}
return nIndexOfUploadedPhoto;
}
- (void) proceedWithUpload {
NSMutableArray* removeArray = [[NSMutableArray alloc] init];
dispatch_group_t taskGroup = dispatch_group_create();
for (int i = 0; i < [_arrPhotoQueue count]; i++) {
dispatch_group_enter(taskGroup);
UIImage * image = [UIImage imageWithContentsOfFile:[_arrPhotoQueue[i] objectForKey:@"imagePath"]];
NSString * callKey = [_arrPhotoQueue[i] objectForKey:@"callKey"];
[self processImage:image :callKey completed:^(bool bSuccess){
if (bSuccess) {
//photo is uploaded successfully
int nPhotoIdx = [self removePhotoFromMemory:callKey];
if (nPhotoIdx > -1) {
[removeArray addObject:[NSString stringWithFormat:@"%d", nPhotoIdx]];
}
dispatch_group_leave(taskGroup);
} else {
dispatch_group_leave(taskGroup);
}
}];
}
// Here we wait for all the requests to finish
dispatch_group_notify(taskGroup, dispatch_get_main_queue(), ^{
// run code when all files are downloaded
[self removePhotoFromQueue:removeArray];
});
}
- (void) removePhotoFromQueue:(NSMutableArray*)index {
NSArray *sortedIndex = [index sortedArrayUsingComparator:^(id obj1, id obj2){
if ([obj1 isKindOfClass:[NSString class]] && [obj2 isKindOfClass:[NSString class]]) {
int nFirstIndex = (int)[obj1 integerValue];
int nSecondIndex = (int)[obj2 integerValue];
if (nFirstIndex > nSecondIndex) {
return (NSComparisonResult)NSOrderedAscending;
} else if (nFirstIndex < nSecondIndex) {
return (NSComparisonResult)NSOrderedDescending;
}
}
return (NSComparisonResult)NSOrderedSame;
}];
for (int i =0; i < [sortedIndex count]; i++) {
int nIdx = (int)[sortedIndex[i] integerValue];
[_arrPhotoQueue removeObjectAtIndex:nIdx];
}
[defaults removeObjectForKey:@"PendingPhotos"];
[defaults setObject:_arrPhotoQueue forKey:@"PendingPhotos"];
[defaults synchronize];
}
#pragma mark Image Processor
- (void) processImage:(UIImage*)image :(NSString*)callKey completed:(void(^)(bool bSuccess)) block{
__block NSMutableDictionary * message;
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
message = [self uploadImage:image CallKey:callKey];
[self uploadImage:image CallKey:callKey];
dispatch_async(dispatch_get_main_queue(), ^{
if ([[message objectForKey:@"status"] isEqualToString:@"success"]) {
block(true);
} else {
NSLog(@"Somethign is wrong");
block(false);
}
});
});
}
- (NSMutableDictionary*) uploadImage:(UIImage *)theimage CallKey:(NSString*)callKey {
NSData * imageData = UIImageJPEGRepresentation([self fixrotation:theimage], 90);
NSString * jsonString= [JSONCommunicator buildJSONForImageUpload:[self prepareDictionaryDataForUploadWithKey:callKey]];
NSURL * uploadURL = [NSURL URLWithString:MYURL];
// create the URL
NSMutableURLRequest *postRequest = [NSMutableURLRequest requestWithURL:uploadURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0];
[postRequest setHTTPMethod:@"POST"];
// just some random text that will never occur in the body
NSString *boundary = @"---------------------------14737809831466499882746641449";
NSString *headerBoundary = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
[postRequest addValue:headerBoundary forHTTPHeaderField:@"Content-Type"];
// create data
NSMutableData *postBody = [NSMutableData data];
// JSON part
[postBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[@"Content-Disposition: form-data; name=\"params\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[jsonString dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
// media part
[postBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"UploadedFile\"; filename=\"image.jpg\"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[@"Content-Type: image/jpeg\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:imageData];
[postBody appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
// final boundary
[postBody appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
NSString *s = [[NSString alloc] initWithData:postBody encoding:NSASCIIStringEncoding];
//NSLog(@"Image String: %@", s);
// add body to post
[postRequest setHTTPBody:postBody];
// pointers to some necessary objects
NSURLResponse* response;
NSError* error;
// synchronous filling of data from HTTP POST response
NSData *responseData = [NSURLConnection sendSynchronousRequest:postRequest returningResponse:&response error:&error];
if (error) {
NSLog(@"Error: %@", [error localizedDescription]);
}
// convert data into string
NSString *responseString = [[NSString alloc] initWithBytes:[responseData bytes] length:[responseData length] encoding:NSUTF8StringEncoding];
NSLog(@"TOPS Response String: %@", responseString);
NSData *data = [responseString dataUsingEncoding:NSUTF8StringEncoding];
NSMutableDictionary * dictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
return dictionary;
}