POSInputStreamLibrary 2.3.5

POSInputStreamLibrary 2.3.5

测试已测试
Lang语言 Obj-CObjective C
许可 MIT
Released最后发布2018年5月

Pavel Osipov 维护。



  • Pavel Osipov

NSInputStream for ALAsset

Version

POSInputStreamLibrary 包含使用 ALAsset 作为其数据源的 NSInputStream 实现。主特性如下:

  • 同步和异步工作模式。
  • ALAsset 无效化后自动刷新。
  • 在读取数据时智能缓存 ALAsset
  • 使用 NSStreamFileCurrentOffsetKey 属性指定读偏移。
  • ALAssetRepresentation 无效化后自动恢复。
  • 检测并应用 iOS 7 和 iOS 8(2.0.0 版本中新增)调整过滤器。
  • 与 CFNetwork 框架集成。
  • 与 AFNetworking 集成(感谢 @bancek)。

NSInputStream 的类别定义了最常见的初始化器

@interface NSInputStream (POS)
+ (NSInputStream *)pos_inputStreamWithAssetURL:(NSURL *)assetURL;
+ (NSInputStream *)pos_inputStreamWithAssetURL:(NSURL *)assetURL asynchronous:(BOOL)asynchronous;
+ (NSInputStream *)pos_inputStreamForCFNetworkWithAssetURL:(NSURL *)assetURL;
+ (NSInputStream *)pos_inputStreamForAFNetworkingWithAssetURL:(NSURL *)assetURL;
@end

都需要 ALAssetNSURL。《POSBlobInputStream》将在打开时查询 ALAssetLibrary 以获取 ALAsset

工作模式

同步

在同步模式下,所有 POSInputStream 的方法都会在调用期间完全执行它们的工作。如果需要从 ALAssetLibrary 获取一些数据,调用线程将被阻塞。这使您可以在不订阅其事件的情况下处理流,但与此同时,不应从主线程调用 NSInputStream 的任何方法。原因是 ALAssetLibrary 与主线程上的客户端代码交互。因此,如果 POSBlobInputStream 在阻塞的主线程中等待来自 ALAssetLibrary 的答案,将会出现死锁。以下是在同步模式下使用 POSBlobInputStream 计算具有 ALAsset 校验和的示例。

NSInputStream *stream = [NSInputStream pos_inputStreamWithAssetURL:assetURL asynchronous:NO];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [stream open];
    if ([stream streamStatus] == NSStreamStatusError) {
        // Error notification
        [stream close];
        return;
    }
    NSParameterAssert([stream streamStatus] == NSStreamStatusOpen);
    while ([stream hasBytesAvailable]) {
        uint8_t buffer[kBufferSize];
        const NSInteger readCount = [stream read:buffer maxLength:kBufferSize];
        if (readCount < 0) {
            break;
        } else {
            // Checksum update
        }
    }
    if ([stream streamStatus] != NSStreamStatusAtEnd) {
        // Error notification
    }
    [stream close];
}

异步操作

在异步模式下,所有 POSBlobInputStream 的方法在调用后立即返回。客户端代码应向流提供一个代理以接收有关其状态的信息。这是了解流何时打开、何时有可读数据以及出错的唯一方式。以下是在以下代码中查看异步版本的校验和计算。

@interface ChecksumCalculator () <NSStreamDelegate>
@end

@implementation ChecksumCalculator

- (void)calculateChecksumForStream:(NSInputStream *)aStream {
    aStream.delegate = self;
    [aStream open];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ @autoreleasepool {
        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        [aStream scheduleInRunLoop:runLoop forMode:NSDefaultRunLoopMode];
        for (;;) { @autoreleasepool {
            if (![runLoop runMode:NSDefaultRunLoopMode
                       beforeDate:[NSDate dateWithTimeIntervalSinceNow:kRunLoopInterval]]) {
                break;
            }
            const NSStreamStatus streamStatus = [aStream streamStatus];
            if (streamStatus == NSStreamStatusError || streamStatus == NSStreamStatusClosed) {
                    break;
            }
        }}
    }});
}

#pragma mark - NSStreamDelegate

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
    switch (eventCode) {
        case NSStreamEventHasBytesAvailable: {
            [self updateChecksumForStream:aStream];
        } break;
        case NSStreamEventEndEncountered: {
            [self notifyChecksumCalculationCompleted];
            [aStream close];
        } break;
        case NSStreamEventErrorOccurred: {
            [self notifyErrorOccurred:[aStream streamError]];
            [aStream close];
        } break;
    }
}

@end

与 NSURLRequest 集成

POSBlobInputStream 为 NSURLRequest 集成提供了 pos_inputStreamForCFNetworkWithAssetURL 初始化器。它考虑了以下 CFNetwork "功能"。

  • CFNetwork 在同步模式下与流一起工作。
  • CFNetowrk 使用已弃用的 CFReadStreamGetError 方法从流中获取错误描述。由于 NSInputStream 的 "toll-free bridging" 实现中的错误,此操作将使应用程序崩溃。这就是为什么 streamStatus 方法永远不会返回 NSStreamStatusError 的原因。此外,POSBlobInputStream 不会通过 C 回调通知其状态更改。接收流实际状态的唯一方式是通过 NSStreamDelegate 回调。

与 AFNetworking 集成

POSBlobInputStream 为 AFNetworking 集成提供了 pos_inputStreamForAFNetworkingWithAssetURL 初始化器。

示例

- (void) uploadAsset:(ALAsset*)asset toUrl:(NSString*)url
             success:(void (^)(id responseObject))success
             failure:(void (^)(NSError* error))failure {
    ALAssetRepresentation *assetRepresentation = asset.defaultRepresentation;
    NSString* assetFilename = assetRepresentation.filename;
    NSURL *assetUrl = assetRepresentation.url;
    unsigned long long assetSize = assetRepresentation.size;
    NSInputStream *assetInputStream = [NSInputStream pos_inputStreamForAFNetworkingWithAssetURL:assetUrl];
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    AFHTTPRequestOperation *op = [manager POST:url parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
        [formData appendPartWithInputStream:assetInputStream name:@"file" fileName:assetFilename length:assetSize mimeType:@"image/jpeg"];
    } success:^(AFHTTPRequestOperation *operation, id responseObject) {
        success(responseObject);
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        failure(error);
    }];
}

请参阅 完整示例 以获取更多详细信息。

资源