代码之家  ›  专栏  ›  技术社区  ›  Richard J. Ross III

nsfilehandlefilehandleforreadingfromurl有人能给我解释一下吗?

  •  1
  • Richard J. Ross III  · 技术社区  · 14 年前

    我正在尝试根据Dave DeLong的一个定制输入流 here 这也允许通过NSURL从服务器读取数据。到目前为止,我采用了这种方法,这种方法对本地文件很有效:

    @interface RJRStreamReader : NSObject {
        @private
        NSFileHandle *fileHandle;
        NSStringEncoding encoding;
        NSString *lineDelimiter;
        unsigned long long currentOffset;
        unsigned long long totalFileLength;
        int chunkSize;
    }
    
    @property(readwrite, assign) NSStringEncoding encoding;
    @property(readwrite, assign) unsigned long long currentOffset;
    @property(readwrite, copy) NSString *lineDelimiter;
    @property(readwrite, assign) int chunkSize;
    @property(readonly) unsigned long long totalFileLength;
    
    -(id) initWithLocalFile:(NSString *) fileName;
    
    -(id) initWithURL:(NSURL *) remoteURL;
    
    -(id) initWithFileHandle:(NSFileHandle *) fh;
    
    -(NSString *) readToEnd;
    
    -(NSString *) readLine;
    
    /**
     @summary Reads a block of bytes from a stream
     @param blockLen the number of bytes to read
     @returns a string containing the data from the bytes read
     */
    -(NSString *) readBlock:(int) blockLen;
    
    @end
    

    以及我的实现:

    @implementation RJRStreamReader
    
    @synthesize currentOffset, lineDelimiter, encoding, chunkSize, totalFileLength;
    
    -(id) initWithLocalFile:(NSString *)fileName
    {
        if (self = [super init])
        {
            fileHandle = [NSFileHandle fileHandleForReadingAtPath:fileName];
            if (fileHandle == nil)
            {
                [self release];
                return nil;
            }
    
            chunkSize = 10;
            encoding = NSUTF8StringEncoding;
            lineDelimiter = [[NSString alloc] initWithString:@"\n"];
            [fileHandle retain];
            currentOffset = 0ULL;
            [fileHandle seekToEndOfFile];
            totalFileLength = [fileHandle offsetInFile];
        }
    
        return self;
    }
    
    -(id) initWithURL:(NSURL *)remoteURL
    {
        if (self = [super init])
        {
            NSError *err = nil;
            fileHandle = [NSFileHandle fileHandleForReadingFromURL:remoteURL error:&err];
            if (err)
            {
                NSLog(@"Error occurred, aborting. Details: %@", err);
                [self release];
                return nil;
            }
    
            chunkSize = 10;
            encoding = NSUTF8StringEncoding;
            lineDelimiter = [[NSString alloc] initWithString:@"\n"];
            [fileHandle retain];
            currentOffset = 0ULL;
            [fileHandle seekToEndOfFile];
            totalFileLength = [fileHandle offsetInFile];
        }
    
        return self;
    }
    
    -(id) initWithFileHandle:(NSFileHandle *) fh
    {
        if (self = [super init])
        {
            fileHandle = fh;
            if (!fh)
            {
                [self release];
                [NSException raise:@"FH cannot be nil!" format:@"FH cannot be nil!"];
                return nil;
            }
    
            chunkSize = 10;
            encoding = NSUTF8StringEncoding;
            lineDelimiter = [[NSString alloc] initWithString:@"\n"];
            [fileHandle retain];
            currentOffset = 0ULL;
            [fileHandle seekToEndOfFile];
            totalFileLength = [fileHandle offsetInFile];
        }
    
        return self;
    }
    
    -(NSString *) readBlock:(int)blockLen
    {
        if (currentOffset >= totalFileLength) { return nil; }
    
        [fileHandle seekToFileOffset:currentOffset];
        NSData *data = [fileHandle readDataOfLength:blockLen];
        currentOffset += blockLen;
        [fileHandle seekToFileOffset:currentOffset];
    
        return [[[NSString alloc] initWithData:data encoding:encoding] autorelease];
    }
    
    -(NSString *) readLine
    {
        /* 
           if you want to see the code for this method, visit this link: 
           https://stackoverflow.com/questions/3707427/how-to-read-data-from-nsfilehandle-line-by-line
           it is exactly the same
         */
    }
    
    -(NSString *) readToEnd
    {
        if (currentOffset >= totalFileLength) { return nil; }
    
        [fileHandle seekToFileOffset:currentOffset];
        NSData *data = [fileHandle readDataToEndOfFile];
        currentOffset = totalFileLength;
        [fileHandle seekToEndOfFile];
    
        return [[[NSString alloc] initWithData:data encoding:encoding] autorelease];
    }
    
    -(void) dealloc
    {
        [fileHandle closeFile];
        [fileHandle release];
        [lineDelimiter release];
        [super dealloc];
    }
    
    @end
    

    问题是,当我试图这样使用它时:

    RJRStreamReader *stream = [[RJRStreamReader alloc] initWithURL:[NSURL URLWithString:@"http://www.stackoverflow.com/robots.txt"];
    NSString *s = [stream readToEnd];
    

    s 总是空的,因为 NSFileHandle [NSFileHandle fileHandleForReadingFromURL:remoteURL] 似乎总是返回零,没有任何错误。

    这是我的代码中的一个bug还是他们的一个未记录的特性?

    谢谢

    1 回复  |  直到 7 年前
        1
  •  1
  •   Richard J. Ross III    6 年前

    好吧,这是我的问题:

    Documentation

    fileHandleForReadingFromURL:错误:

    它不读取服务器的URL,这导致了我采用这种方法(我希望人们可以为我编辑它,因为它需要延迟加载、优化等)

    RJRStreamReader.h:

    @interface RJRStreamReader : NSObject {
        @private
        NSData *data;
        NSStringEncoding encoding;
        NSString *lineDelimiter;
        unsigned long long currentOffset;
        unsigned long long totalFileLength;
        int chunkSize;
    }
    
    @property(readwrite, assign) NSStringEncoding encoding;
    @property(readwrite, assign) unsigned long long currentOffset;
    @property(readwrite, copy) NSString *lineDelimiter;
    @property(readwrite, assign) int chunkSize;
    @property(readonly) unsigned long long totalFileLength;
    
    -(id) initWithLocalFile:(NSString *) fileName;
    
    -(id) initWithURL:(NSURL *) remoteURL;
    
    -(id) initWithFileHandle:(NSFileHandle *) fh;
    
    -(id) initWithData:(NSData *) theData;
    
    -(NSString *) readToEnd;
    
    -(NSString *) readLine;
    
    /**
     @summary Reads a block of bytes from a stream
     @param blockLen the number of bytes to read
     @returns a string containing the data from the bytes read
     */
    -(NSString *) readBlock:(int) blockLen;
    
    @end
    

    @implementation RJRStreamReader
    
    @synthesize currentOffset, lineDelimiter, encoding, chunkSize, totalFileLength;
    
    -(id) initWithLocalFile:(NSString *)fileName
    {
        if (self = [super init])
        {
            NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:fileName];
    
            if (fileHandle == nil)
            {
                [self release];
                return nil;
            }
    
            data = [[fileHandle readDataToEndOfFile] retain];
            chunkSize = 10;
            encoding = NSUTF8StringEncoding;
            lineDelimiter = [[NSString alloc] initWithString:@"\n"];
            currentOffset = 0ULL;
            [fileHandle seekToEndOfFile];
            totalFileLength = [fileHandle offsetInFile];
            [fileHandle closeFile];
        }
    
        return self;
    }
    
    -(id) initWithURL:(NSURL *)remoteURL
    {
        if (self = [super init])
        {
            NSError *err = nil;
            NSURLResponse *resp = nil;
    
            data = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:remoteURL] returningResponse:&resp error:&err];
    
            if (err)
            {
                NSLog(@"Error occurred, aborting. Details: %@", err);
                [self release];
                return nil;
            }
    
            chunkSize = 10;
            encoding = NSUTF8StringEncoding;
            lineDelimiter = [[NSString alloc] initWithString:@"\n"];
            [data retain];
            currentOffset = 0ULL;
            totalFileLength = [data length];
        }
    
        return self;
    }
    
    -(id) initWithFileHandle:(NSFileHandle *) fh
    {
        if (self = [super init])
        {
            if (!fh)
            {
                [self release];
                [NSException raise:@"FH cannot be nil!" format:@"FH cannot be nil!"];
                return nil;
            }
    
            unsigned long long pos = [fh offsetInFile];
    
            data = [[fh readDataToEndOfFile] retain];       
            chunkSize = 10;
            encoding = NSUTF8StringEncoding;
            lineDelimiter = [[NSString alloc] initWithString:@"\n"];
            currentOffset = 0ULL;
            [fh seekToEndOfFile];
            totalFileLength = [fh offsetInFile];
            [fh seekToFileOffset:pos];
        }
    
        return self;
    }
    
    -(id) initWithData:(NSData *)theData
    {
        if (self = [super init])
        {
            data = [theData retain];    
            chunkSize = 10;
            encoding = NSUTF8StringEncoding;
            lineDelimiter = [[NSString alloc] initWithString:@"\n"];
            currentOffset = 0ULL;
            totalFileLength = [data length];        
        }
    
        return self;
    }
    
    -(NSString *) readBlock:(int)blockLen
    {
        if (currentOffset >= totalFileLength) { return nil; }
    
        NSData *tmpdata = [data subdataWithRange:NSMakeRange(currentOffset, blockLen)];
        currentOffset += blockLen;
    
        return [[[NSString alloc] initWithData:tmpdata encoding:encoding] autorelease];
    }
    
    -(NSString *) readLine
    {
        if (currentOffset >= totalFileLength) { return nil; }
    
        NSData * newLineData = [lineDelimiter dataUsingEncoding:NSUTF8StringEncoding];
        //[fileHandle seekToFileOffset:currentOffset];
        NSMutableData * currentData = [[NSMutableData alloc] init];
        BOOL shouldReadMore = YES;
    
        NSAutoreleasePool * readPool = [[NSAutoreleasePool alloc] init];
        while (shouldReadMore) {
            if (currentOffset >= totalFileLength) { break; }
            NSData *chunk = [data subdataWithRange:NSMakeRange(currentOffset, chunkSize)];
            NSRange newLineRange = [chunk rangeOfData_dd:newLineData];
            if (newLineRange.location != NSNotFound) {
    
                //include the length so we can include the delimiter in the string
                chunk = [chunk subdataWithRange:NSMakeRange(0, newLineRange.location)];
                shouldReadMore = NO;
            }
            [currentData appendData:chunk];
            currentOffset += [chunk length];
        }
        [readPool release];
    
        NSString * line = [[NSString alloc] initWithData:currentData encoding:NSUTF8StringEncoding];
        [currentData release];
        return [line autorelease];
    }
    
    -(NSString *) readToEnd
    {
        if (currentOffset >= totalFileLength) { return nil; }
    
        NSData *tmpdata = [data subdataWithRange:NSMakeRange(currentOffset, totalFileLength - currentOffset)];
        currentOffset = totalFileLength;
    
        return [[[NSString alloc] initWithData:tmpdata encoding:encoding] autorelease];
    }
    
    -(void) dealloc
    {
        [data release];
        [lineDelimiter release];
        [super dealloc];
    }
    
    @end