前言
大文件上传或者下载,如果一次性加载数据到内存,会导致内存暴涨,所以需要使用输入流 NSInputStream
和输出流 NSOutputStream
,建立起文件和内存中的管道,通过管道输入和输出数据。
在 Cocoa
中包含三个与流相关的类:NSStream
、NSInputStream
和 NSOutputStream
。NSStream
是一个抽象基类,定义了所有流对象的基础接口和属性。NSInputStream
和 NSOutputStream
继承自 NSStream
,实现了输入流和输出流的默认行为。从下图中可以看出,NSInputStream
可以从文件、socket
和 NSData
对象中获取数据;NSOutputStream
可以将数据写入文件、socket
、内存缓存和 NSData
对象中。
流的使用
这里就简单的写了一个 demo:从 main bundle
中读取视频文件,保存到沙盒中;
核心代码如下
#import "ViewController.h"
@interface ViewController () <NSStreamDelegate> {
NSInteger _count;
}
@property (nonatomic, strong) NSMutableData *data;
@property (nonatomic, strong) NSInputStream *inputStream;
// 沙盒路径
@property (nonatomic, strong) NSString *fullPath;
// 输出流
@property (nonatomic, strong) NSOutputStream *outPutStream;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
_count = 0;
[self testNSInputStream];
}
- (void)testNSInputStream {
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"mp4"];
NSData *data = [NSData dataWithContentsOfFile:filePath];
self.inputStream = [NSInputStream inputStreamWithData:data];
// inputStreamWithFileAtPath: 这个方法使用时有问题,读取不到数据;但是通过inputStreamWithData:方法读取就没什么问题
// self.inputStream = [NSInputStream inputStreamWithFileAtPath:filePath];
self.inputStream.delegate = self;
[self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[self.inputStream open];
// 写数据到沙盒中
self.fullPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"test.mp4"];
NSLog(@"%@", self.fullPath);
self.outPutStream = [[NSOutputStream alloc] initToFileAtPath:self.fullPath append:YES];
// 打开输入流
[self.outPutStream open];
}
/**
NSStreamEventOpenCompleted:文件打开成功
NSStreamEventHasBytesAvailable:读取到数据
NSStreamEventEndEncountered:文件读取结束
*/
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
switch(eventCode) {
case NSStreamEventOpenCompleted:
NSLog(@"文件打开,准备读取数据");
break;
case NSStreamEventHasBytesAvailable: {
uint8_t buf[1024];
NSInteger readLength = [self.inputStream read:buf maxLength:1024];
if (readLength > 0) {
if (_count == 0) {
NSLog(@"数据读取中...");
}
_count++;
[self.data appendBytes:(const void *)buf length:readLength];
} else {
NSLog(@"未读取到数据");
}
break;
}
case NSStreamEventEndEncountered: {
NSLog(@"数据读取结束");
// 写数据
[self.outPutStream write:self.data.bytes maxLength:self.data.length];
[aStream close];
[aStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
aStream = nil;
[self.outPutStream close];
self.outPutStream = nil;
break;
}
default:
break;
}
}
- (NSMutableData *)data {
if (_data == nil) {
_data = [NSMutableData data];
}
return _data;
}
@end
Author
如果你有什么建议,可以关注我,直接留言,留言必回。