self.FfAppClient
中的两种方法(一种用于上载和一种用于下载)在单独的App中工作,其中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
块中给出了无意义的字符串,例如;[email protected]!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:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", @"docfile", filename] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[@"Content-Type: image/jpeg\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:content];
[body appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"--%@--\r\n", 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);
}];
}
但是,相比真正的解决方案,这是相当快速的hack。