【问题标题】:How to download a file and save it to the documents directory with AFNetworking?如何使用 AFNetworking 下载文件并将其保存到文档目录?
【发布时间】:2011-12-04 01:56:47
【问题描述】:

我正在使用 AFNetworking 库。我不知道如何下载文件并将其保存到文档目录。

【问题讨论】:

    标签: ios iphone afnetworking


    【解决方案1】:
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"..."]];
    AFHTTPRequestOperation *operation = [[[AFHTTPRequestOperation alloc] initWithRequest:request] autorelease];
    
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"filename"];
    operation.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:NO];
    
    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
        NSLog(@"Successfully downloaded file to %@", path);
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        NSLog(@"Error: %@", error);
    }];
    
    [operation start];
    

    【讨论】:

    • 你甚至可以添加一个进度块: //设置上传块返回文件上传进度 [操作 setDownloadProgressBlock:^(NSInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToRead) { float progress = totalBytesWritten / (float)totalBytesExpectedToRead; NSLog(@"下载百分比:%f %%", 进度*100); }];
    • 请注意,上面的代码会将服务器的任何响应下载到输出流中,因此如果服务器响应状态码为 404,则 404 页面将保存到指定的路径。您必须检查 operation.response.statusCode 的成功块。
    • 不用手动检查状态码,直接用operation.hasAcceptableStatusCode
    • NSURLRequest 如何发布或获取一个网址?
    • 如何只在成功块中下载? @leolobato 你能建议吗?
    【解决方案2】:

    我要跳出@mattt 的回答,并使用AFHTTPRequestOperationManager 发布AFNetworking 2.0 的版本。

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"filename"];
    
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    AFHTTPRequestOperation *op = [manager GET:@"http://example.com/file/to/download" 
                                   parameters:nil
        success:^(AFHTTPRequestOperation *operation, id responseObject) {
             NSLog(@"successful download to %@", path);
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
             NSLog(@"Error: %@", error);
        }];
    op.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:NO];
    

    【讨论】:

    • 我绝对推荐使用较新的 AFNetworking 库以这种方式进行操作
    • @swilliams [AFHTTPRequestOperationManager manager] 将使用默认的 AFJSONResponseSerializer 创建新的管理器对象,你想要吗?我认为最好创建 AFNoneResponseSerialize 让文件保持不变
    • 这里怎么获取下载进度参数?
    • shshnk,看看下面我的回答
    • 有没有办法在下载未完成时自动清理文件?
    【解决方案3】:

    我说的是AFNetworking 2.0

    [AFHTTPRequestOperationManager manager] 使用默认的 AFJSONResponseSerializer 创建管理器对象,并执行内容类型限制。看看这个

    - (BOOL)validateResponse:(NSHTTPURLResponse *)response
                        data:(NSData *)data
                       error:(NSError * __autoreleasing *)error
    

    所以我们需要创建一个无响应序列化程序并像往常一样使用AFHTTPRequestOperationManager。

    这里是 AFNoneResponseSerializer

    @interface AFNoneResponseSerializer : AFHTTPResponseSerializer
    
    + (instancetype)serializer;
    
    @end
    
    @implementation AFNoneResponseSerializer
    
    #pragma mark - Initialization
    + (instancetype)serializer
    {
        return [[self alloc] init];
    }
    
    - (instancetype)init
    {
        self = [super init];
    
        return self;
    }
    
    #pragma mark - AFURLResponseSerializer
    - (id)responseObjectForResponse:(NSURLResponse *)response
                               data:(NSData *)data
                              error:(NSError *__autoreleasing *)error
    
    {
        return data;
    }
    
    @end
    

    用法

    self.manager = [AFHTTPRequestOperationManager manager];
    self.manager.responseSerializer = [AFNoneResponseSerializer serializer];
    
    [self.manager GET:@"https://sites.google.com/site/iphonesdktutorials/xml/Books.xml"
               parameters:parameters
                  success:^(AFHTTPRequestOperation *operation, id responseObject)
        {
            if (success) {
                success(responseObject);
            }
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            if (failure) {
                failure(error);
            }
        }];
    

    这样我们就可以得到整个文件而无需任何序列化

    【讨论】:

      【解决方案4】:

      Documentation page 有“创建下载任务”部分的示例:

      NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
      AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
      
      NSURL *URL = [NSURL URLWithString:@"http://example.com/download.zip"];
      NSURLRequest *request = [NSURLRequest requestWithURL:URL];
      
      NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
          NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
          return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
      } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
          NSLog(@"File downloaded to: %@", filePath);
      }];
      [downloadTask resume];
      

      注意!代码适用于 iOS 7+(使用 AFNetworking 2.5.1 测试)

      【讨论】:

      • 当我下载视频并点击返回时没有下载完成。然后我再次点击视频,那个时候我得到这个错误=>MediaPlayerErrorDomain Code=-11800 ...所以请帮助我
      【解决方案5】:

      来自AFNetworking 文档。 将加载的文件保存到您的文档中。 AFNetworking 3.0

      NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
      AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
      
      NSURL *URL = [NSURL URLWithString:@"http://example.com/download.zip"];
      NSURLRequest *request = [NSURLRequest requestWithURL:URL];
      
      NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
          NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
          return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
      } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
          NSLog(@"File downloaded to: %@", filePath);
      }];
      [downloadTask resume];
      

      【讨论】:

        【解决方案6】:
        AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
        
        manager.responseSerializer = [AFCompoundResponseSerializer serializer];
        
        manager.responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"application/octet-stream"];
        
        AFHTTPRequestOperation *operation = [manager GET:url   parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
            if (responseObject) {
                // your code here
            } else {
                // your code here
            }
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        
        }];
        
        [operation start];
        

        // manager.responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"application/octet-stream"];可能因您的期望而异

        【讨论】:

          【解决方案7】:

          是的,最好使用AFNetworking 2.0 方式和AFHTTPRequestOperationManager。使用旧方式我的文件确实下载了,但由于某种原因没有在文件系统中更新。

          附加到 swilliam 的 答案以显示下载进度,在 AFNetworking 2.0 中您也可以这样做 - 只需在设置输出流后设置下载进度块。

          __weak SettingsTableViewController *weakSelf = self;
          
          operation.outputStream = [NSOutputStream outputStreamToFileAtPath:newFilePath append:NO];
          
          [operation setDownloadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToRead) {
          
              float progress = totalBytesWritten / (float)totalBytesExpectedToRead;
          
              NSString *progressMessage = [NSString stringWithFormat:@"%@ \n %.2f %% \n %@ / %@", @"Downloading ...", progress * 100, [weakSelf fileSizeStringWithSize:totalBytesWritten], [weakSelf fileSizeStringWithSize:totalBytesExpectedToRead]];
          
              [SVProgressHUD showProgress:progress status:progressMessage];
          }];
          

          这是我创建字节字符串的方法:

          - (NSString *)fileSizeStringWithSize:(long long)size
          {
              NSString *sizeString;
              CGFloat f;
          
              if (size < 1024) {
                  sizeString = [NSString stringWithFormat:@"%d %@", (int)size, @"bytes"];
              }
              else if ((size >= 1024)&&(size < (1024*1024))) {
                  f = size / 1024.0f;
                  sizeString = [NSString stringWithFormat:@"%.0f %@", f, @"Kb"];
              }
              else if (size >= (1024*1024)) {
                  f = size / (1024.0f*1024.0f);
                  sizeString = [NSString stringWithFormat:@"%.0f %@", f, @"Mb"];
              }
          
              return sizeString;
          }
          

          【讨论】:

          • 当我下载视频并点击返回时没有下载完成。然后我再次点击视频,那个时候我得到这个错误=>MediaPlayerErrorDomain Code=-11800 ...所以请帮助我
          【解决方案8】:

          除了之前的答案,对于 AFNetworking 2.5.0 和 iOS7/8,我发现还需要额外的打开输出流的步骤来防止应用挂起(并最终因内存不足而崩溃) .

          operation.outputStream = [NSOutputStream outputStreamToFileAtPath:dest
                                                                    append:NO];
          [operation.outputStream open];
          [operation start];
          

          【讨论】:

            猜你喜欢
            • 2012-08-03
            • 2016-05-10
            • 1970-01-01
            • 2015-08-28
            • 2012-01-10
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多