iOS NSURLSessionUploadTask 响应数据

Posted

技术标签:

【中文标题】iOS NSURLSessionUploadTask 响应数据【英文标题】:iOS NSURLSessionUploadTask response data 【发布时间】:2014-07-17 11:26:17 【问题描述】:

我已经成功实现了 NSURLSessionUploadTask 并在后台和前台工作。但是读取响应数据时出现问题。

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data

    NSLog(@"1 DATA:\n%@\nEND DATA\n", [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding]);

    [self.responseData appendData:data];


- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error

    if (!error)                         
        NSLog(@"AT THE END DATA:\n%@\nEND DATA\n", [[NSString alloc] initWithData: self.responseData encoding: NSUTF8StringEncoding]);

            [self parsingJSONResponse:self.responseData];

     else 
        NSLog(@"HTTP uploading error : %@", error);
    

这些是以上两个 NSLog 的输出

1 数据: "成功":true,"数据":["uuid":"8BE7DF37-9DA1-44D2-B48C-D012F699A9B1","id":266626,"uuid":"3406D865-1A41-4FC6-BA0B- 0638F17757CC","id":266656],"errors":[],"entityName":"LeadProfile" 结束数据

最后数据: "成功":true,"数据":["uuid":"8BE7DF37-9DA1-44D2-B48C-D012F699A9B1","id":266626,"uuid":"3406D865-1A41-4FC6-BA0B- 0638F17757CC","id":266656],"errors":[],"entityName":"LeadProfile""success":true,"data":["uuid":"8BE7DF37-9DA1-44D2- B48C-D012F699A9B1","id":266626,"uuid":"3406D865-1A41-4FC6-BA0B-0638F17757CC","id":266656],"errors":[],"entityName":"LeadProfile " 结束数据

我想知道为什么这会给我一个上传任务两个不同的响应。 self.responseData 在每个位置上有何不同?

有人认为这是因为苹果网站上提到的原因吗? (因为 NSData 对象通常是由多个不同的数据对象拼凑而成,所以尽可能使用 NSData 的 enumerateByteRangesUsingBlock: 方法来遍历数据而不是使用 bytes 方法(将 NSData 对象扁平化为单个内存块)@987654321 @

【问题讨论】:

【参考方案1】:

你问:

我想知道为什么这会给我一个上传任务两个不同的响应。 self.responseData 在每个位置上有何不同?

这无疑是因为responseData 没有正确或在正确的时间实例化。 (我倾向于在didReceiveResponse 中执行此操作。)请注意,您不会在didReceiveData 中查看responseData。您正在查看data。我建议在didReceiveData 中附加data 后立即检查responseData,我相信你也会在那里看到它加倍。问题是为什么它没有正确实例化/初始化。

有人认为这是因为苹果网站上提到的原因吗?

“因为NSData对象通常是由多个不同的数据对象拼凑而成,所以尽可能使用NSDataenumerateByteRangesUsingBlock:方法来遍历数据而不是使用bytes方法(这会使NSData 对象到单个内存块中)。”

不,这是一个完全不相关的问题。我敢肯定这里的问题要平凡得多。

很遗憾,我们这里没有足够的代码来诊断确切的问题。

【讨论】:

【参考方案2】:

您仅收到部分数据。您的委托方法应连接交付的每个数据对象的内容,以使用 NSMutableData 对象为 URL 加载构建完整数据。

【讨论】:

self.responseData 是一个 NSMutableData ,每次收到数据时我都会附加它。您是否建议使用 NSMutableData 的任何其他方式? 检查任务状态:task.state == NSURLSessionTaskStateCompleted 并确保 countOfBytesExpectedToReceive 和 countOfBytesReceived 相等。除此之外,它可能与请求连接是否保持活动有关,甚至与您解析 json 的方式有关...... 顺便说一句,你不能总是依赖expectedContentLength。正如NSURLResponse 的文档所说:“一些协议实现将内容长度作为响应的一部分进行报告,但并非所有协议都保证能提供这么多数据。客户端应该准备好处理或多或少的数据。”更糟糕的是,有时他们根本无法确定内容大小,并且会报告一个负数(例如,分块响应或压缩响应)。最重要的是,应该非常谨慎地使用接收到的字节数来确定是否完成。【参考方案3】:

像documentation says一样,接收到的数据可能是不连续的。

所以,这可能是一个可能的实现:

- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveData:(NSData *)data

    if (!self.responseData)
    
        NSUInteger capacity = 0;
        if (dataTask.response.expectedContentLength != NSURLResponseUnknownLength)
        
            capacity = (NSUInteger)dataTask.response.expectedContentLength;
        

        self.responseData = [[NSMutableData alloc] initWithCapacity:capacity];
    

    [data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) 
        [self.responseData appendBytes:bytes length:byteRange.length];
    ];

您对- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error 的实现是正确的。

【讨论】:

【参考方案4】:

我找到了解决这个问题的方法。但这可能不是一个正确的答案。

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data

    //Get Hex string from 'data'. There can be a solution directly add bytes to NSData (using 'enumerateByteRangesUsingBlock') rather than convert to Hex string 

    NSMutableString *string = [NSMutableString stringWithCapacity:data.length * 3];
    [data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop)
        for (NSUInteger offset = 0; offset < byteRange.length; ++offset) 
            uint8_t byte = ((const uint8_t *)bytes)[byteRange.location + offset];
            if (string.length == 0)
                [string appendFormat:@"%02X", byte];
            else
                [string appendFormat:@" %02X", byte];
        
    ];

    //Hex string to NSdata

    NSString *command = string;
    command = [command stringByReplacingOccurrencesOfString:@" " withString:@""];
    NSMutableData *commandToSend= [[NSMutableData alloc] init];
    unsigned char whole_byte;
    char byte_chars[3] = '\0','\0','\0';
    for (int i = 0; i < ([command length] / 2); i++) 
        byte_chars[0] = [command characterAtIndex:i*2];
        byte_chars[1] = [command characterAtIndex:i*2+1];
        whole_byte = strtol(byte_chars, NULL, 16);
        [commandToSend appendBytes:&whole_byte length:1];
    

    NSLog(@"1 >>>>>>>>>>>>>>>>>>> %@", [NSString stringWithUTF8String:[commandToSend bytes]]);


【讨论】:

不,这肯定不是正确的解决方案。事实上,如果NSData 实际上被分割成非连续的块,那么这个解决方案就行不通(因为这是取自another answer,虽然没有正确归因于这是不正确的)。 bytes 的索引是错误的。其他答案已修复。而且,对您来说幸运的是,它不会经常将NSData 拆分为不连续的块,因此您可能没有遇到隐藏在上述代码中的错误。这仅适用于您碰巧不再附加到responseData

以上是关于iOS NSURLSessionUploadTask 响应数据的主要内容,如果未能解决你的问题,请参考以下文章

IO复用阻塞IO非阻塞IO同步IO异步IO

四种IO模型‘阻塞IO/非阻塞IO/信号驱动IO/异步IO‘

5种IO模型阻塞IO和非阻塞IO同步IO和异步IO

网络IO模型:同步IO和异步IO,阻塞IO和非阻塞IO

同步IO异步IO阻塞IO非阻塞IO之间的联系与区别

同步IO异步IO阻塞IO非阻塞IO之间的联系与区别