Pregunta

Estoy tratando de hacer un flujo de entrada personalizada en base fuera del uno por Dave DeLong aquí que también permite la lectura de datos de un servidor a través NSURL. Hasta el momento, no tengo este enfoque, que funciona bien para los archivos locales:

@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

Y mi aplicación:

@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

El problema es que cuando intento utilizar de esta manera:

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

s siempre estará vacía porque el NSFileHandle devuelto por [NSFileHandle fileHandleForReadingFromURL:remoteURL] siempre parece volver a cero, sin ningún error.

Es esto un error en mi código o una característica no documentada de los suyos?

Gracias

¿Fue útil?

Solución

OK, esto es mi problema:

De acuerdo con la Documentación

  

fileHandleForReadingFromURL: Error:   Devuelve un identificador de archivo inicializado para leer el archivo, dispositivo o toma el nombre de la URL especificada.

No leer desde una URL servidores, lo que me llevó a este enfoque (Me gustaría que la gente a editar esto para mí, ya que necesita la carga diferida, optimización, etc.)

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

RJRStreamReader.m:

@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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top