两个方法,一个用于上传,一个用于下载,在self.FfAppClient
工作在一个单独的应用程序中,uploadFile
和downloadFile
是由视图控制器上的按钮触发的。当上传完成时,用户有一个视觉反馈,因此可以继续按下按钮来下载文件。
在下面的应用程序中,我已经测试了uploadFile
和downloadFile
都在那里工作,但是当我使用下面的代码段时,上传有时会失败,下载永远不会工作。
我正试图等到我知道上传完成(self.FfAppClient uploadFile
给出success
),然后开始下载。这是完全错误的方式来使用块,更重要的是,有[self downloadImage:[NSString stringWithFormat:@"ımage id=%d",entryID]];
块内不会工作的原因吗?
- (void)uploadThenDownloadImage:(UIImage*)image usingImagePath:(NSString*)imagePath
{
NSData *imageData = UIImageJPEGRepresentation(image, 1);
[self.FfAppClient uploadFile:imagePath withContent:imageData success:^(id response) {
NSInteger entryID = [[response objectForKey:@"id"] integerValue];
[self downloadImage:[NSString stringWithFormat:@"ımage id=%d",entryID]];
} failure:^(NSError *error) {
NSLog(@"Upload error: %@", error.description);
}];
}
- (void)downloadImage:(NSString*)fileID
{
[self.FfAppClient downloadFile:fileID success:^(id response) {
NSString* suggested = [response objectForKey:@"suggested"];
NSString* temp = [response objectForKey:@"temp"];
[self moveTempFileNamed:suggested toIncomingFolderFromTemporaryLocation:temp];
} failure:^(NSError *error) {
NSLog(@"Download error: %@", error.description);
} progress:^(float prc) {
NSLog(@"Download amount: %@", [NSString stringWithFormat:@"downloaded %.02f", prc]);
}];
}
下载部分的错误是:
-[FfAppClient URLSession:task:didCompleteWithError:]: error: Error Domain=NSURLErrorDomain Code=-1000 "bad URL" UserInfo=0x1383b500 {NSUnderlyingError=0x15370380 "bad URL", NSLocalizedDescription=bad URL}
有时,上传也会失败,因为在success
块中的响应给出了一个无意义的字符串,例如;8@c1jL2gZK3yWziVNuY-h1btzGG.t!u5zoRW2MV
,而不是指服务器上图片的名称。
是否有另一种方法来实现这个或我的代码失败的原因?
* * 编辑:uploadFile
和downloadFile
方法:* * - (void)uploadFile:(NSString *)filename withContent:(NSData*)content success:(FfSuccessBlock)successBlock failure:(FfFailBlock)failBlock
{
if ( ![self isConnected] )
{
NSError *error = [NSError errorWithDomain:@"Not connected" code:0 userInfo:nil];
failBlock(error);
return;
}
NSString *urlFormat = [NSString stringWithFormat:@"%@%@/%@", kFfPicsBasePath, kFfPutFileURL, kFfSubscriptionId];
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
NSMutableURLRequest* request = [self signedRequestWithURL:[NSURL URLWithString:urlFormat] andMethod:@"POST" andParams:params];
[request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
NSString* boundary = [NSString stringWithFormat:@"Boundary+%08X%08X", arc4random(), arc4random()];
[request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary] forHTTPHeaderField:@"Content-Type"];
// Body part for the attachament. This is an image.
NSMutableData *body = [NSMutableData data];
[body appendData:[[NSString stringWithFormat:@"--%@rn", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name="%@"; filename="%@"rn", @"docfile", filename] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[@"Content-Type: image/jpegrnrn" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:content];
[body appendData:[[NSString stringWithFormat:@"rn"] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"--%@--rn", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
request.HTTPBody = body;
self.currentSuccessBlock = successBlock;
self.currentFailBlock = failBlock;
__block FfAppClient* me = self;
self->_resTask = [self->_resSession dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
int statusCode = httpResponse.statusCode;
if ( !error )
{
if ( statusCode >= 400 )
{
NSString* bodyError = data ? [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] : [NSString stringWithFormat:NSLocalizedString(@"HTTP Error: %d", @"FfResourceHTTPErrorDomain description"), statusCode];
NSDictionary *errorInfo =[NSDictionary dictionaryWithObject:bodyError forKey:NSLocalizedDescriptionKey];
error = [NSError errorWithDomain:@"Failed URL" code:statusCode userInfo:errorInfo];
}
else if ( ![[response MIMEType] isEqualToString:@"application/json"] || [data length] == 0 )
{
NSString* bodyStr = data ? [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] : @"";
NSString* bodyError = [NSString stringWithFormat:NSLocalizedString(@"HTTP Content Type Error: %@/n%@", @"FfOResource2HTTPErrorDomain description"), [response MIMEType], bodyStr];
NSDictionary *errorInfo =[NSDictionary dictionaryWithObject:bodyError forKey:NSLocalizedDescriptionKey];
error = [NSError errorWithDomain:@"Failed URL" code:555 userInfo:errorInfo];
}
}
id jsonResponse = nil;
if ( !error )
{
jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
}
if ( error )
{
data = nil;
if ( me.currentFailBlock ) {
__block NSError* blockError = error;
dispatch_sync(dispatch_get_main_queue(), ^{ me.currentFailBlock( blockError ); });
}
}
else
{
if ( me.currentSuccessBlock )
{
dispatch_sync(dispatch_get_main_queue(), ^{ me.currentSuccessBlock(jsonResponse); });
}
}
me->_resTask = nil;
[me clearBlocks];
}];
[self->_resTask resume];
}
- (void)downloadFile:(NSString *)fileId success:(FfSuccessBlock)successBlock failure:(FfFailBlock)failBlock progress:(FfProgressBlock)progress;
{
if ( ![self isConnected] )
{
NSError *error = [NSError errorWithDomain:@"Not connected" code:0 userInfo:nil];
failBlock(error);
return;
}
NSString *urlFormat = [NSString stringWithFormat:@"%@%@/%@", kFfPicsBasePath, kFfGetFileURL, fileId];
NSLog(@"fileid: %@", fileId);
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
NSMutableURLRequest* request = [self signedRequestWithURL:[NSURL URLWithString:urlFormat] andMethod:@"GET" andParams:params];
NSLog(@"---> Request: %@", request);
[request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
self.currentSuccessBlock = successBlock;
self.currentFailBlock = failBlock;
self.currentProgressBlock = progress;
self->_resTask = [self->_resSession downloadTaskWithRequest:request];
[self->_resTask resume];
}
听起来像是内存管理或多线程问题;从块内调用方法是可以的,但可能不是您在这里使用FfAppClient
所做的。在上传_resTask
完全完成之前,您启动了一个下载任务,该任务覆盖了仍然未完成的上传调用所设置的大多数ivars(这可能导致对象被释放),并且该任务在另一个线程上运行。您可能需要对FfAppClient
代码进行重大重写以使其安全,但您可以尝试以下两种快速技巧:
1)尽可能使用两个不同的FfAppClient
注意下面代码中的FfAppClientA
和FfAppClientB
:
- (void)uploadThenDownloadImage:(UIImage*)image usingImagePath:(NSString*)imagePath
{
NSData *imageData = UIImageJPEGRepresentation(image, 1);
[self.FfAppClientA uploadFile:imagePath withContent:imageData success:^(id response) {
NSInteger entryID = [[response objectForKey:@"id"] integerValue];
[self downloadImage:[NSString stringWithFormat:@"ımage id=%d",entryID]];
} failure:^(NSError *error) {
NSLog(@"Upload error: %@", error.description);
}];
}
- (void)downloadImage:(NSString*)fileID
{
[self.FfAppClientB downloadFile:fileID success:^(id response) {
NSString* suggested = [response objectForKey:@"suggested"];
NSString* temp = [response objectForKey:@"temp"];
[self moveTempFileNamed:suggested toIncomingFolderFromTemporaryLocation:temp];
} failure:^(NSError *error) {
NSLog(@"Download error: %@", error.description);
} progress:^(float prc) {
NSLog(@"Download amount: %@", [NSString stringWithFormat:@"downloaded %.02f", prc]);
}];
}
2)异步调度并允许第一个任务在开始第二个任务之前完全完成
当成功和失败块同步分配到main时,尝试以下操作:
- (void)uploadThenDownloadImage:(UIImage*)image usingImagePath:(NSString*)imagePath
{
NSData *imageData = UIImageJPEGRepresentation(image, 1);
[self.FfAppClientA uploadFile:imagePath withContent:imageData success:^(id response) {
dispatch_async(dispatch_get_main_queue(), ^{
NSInteger entryID = [[response objectForKey:@"id"] integerValue];
[self downloadImage:[NSString stringWithFormat:@"ımage id=%d",entryID]];
});
} failure:^(NSError *error) {
NSLog(@"Upload error: %@", error.description);
}];
}
不过,这些都是比较快速的技巧,而不是真正的解决方案。