NSInputStream for ALAsset
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
都需要 ALAsset
的 NSURL
。《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);
}];
}
请参阅 完整示例 以获取更多详细信息。