2010-05-19 11 views
10

Me gustaría descargar archivos directamente de una URL al disco usando Object-C en el sistema operativo del iPhone.¿Cómo descargar archivos directamente al disco en el sistema operativo del iPhone?

Actualmente estoy usando NSURLConnection para enviar una solicitud sincrónica, escribiendo el NSData devuelto en un archivo.

¿Cómo puedo cambiar el manejo de la descarga (aún teniendo la solicitud sincrónica, ya está en un hilo de fondo) para escribir los datos directamente en el disco, sin usar variables de memoria para almacenar el contenido completo (solo partes pequeñas)?

Un código de ejemplo sería apreciado.

¡Gracias a todos por adelantado por sus respuestas!

+0

No va a ser posible descargar literalmente los datos "directamente en el disco", debido a que el sistema operativo no tiene un "Copiar datos de URL' a' a archivo de disco 'b' sin pasar por la memoria" llamada al sistema. Los datos se almacenarán en la memoria en algún punto de la línea. Ahora, es posible que encuentre una biblioteca que proporcione una API de "descarga de URL a archivo", pero internamente sigue usando un búfer en la memoria. –

+0

Eso es cierto David, pero no debería ser necesario almacenar todo el contenido del archivo en la memoria, solo pequeños búferes al escribir en el archivo. Eso es lo que estoy buscando. – favo

Respuesta

13

Puede hacer esto, pero es un poco complicado de configurar. Así es como lo haría:

advertencia: el siguiente código fue escrito en un navegador y compilado en mi memoria. Además, no hay mucho manejo de errores. Implementador de avisos.

//NSURLConnection+DirectDownload.h 
@interface NSURLConnection (DirectDownload) 

+ (BOOL) downloadItemAtURL:(NSURL *)url toFile:(NSString *)localPath error:(NSError **)error; 

@end 

//NSURLConnection+DirectDownload.m 
@implementation NSURLConnection (DirectDownload) 

+ (BOOL) downloadItemAtURL:(NSURL *)url toFile:(NSString *)localPath error:(NSError **)error { 
    NSMutableURLRequest * request = [[NSMutableURLRequest alloc] initWithURL:url]; 
    //configure the request, or leave it as-is 

    DirectDownloadDelegate * delegate = [[DirectDownloadDelegate alloc] initWithFilePath:localPath]; 
    NSURLConnection * connection = [[NSURLConnection alloc] initWithRequest:request delegate:delegate]; 
    [delegate autorelease]; 
    [request release]; 

    while ([delegate isDone] == NO) { 
    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]]; 
    } 

    [connection release]; 

    NSError * downloadError = [delegate error]; 
    if (downloadError != nil) { 
    if (error != nil) { *error = [[downloadError retain] autorelease]; } 
    return NO; 
    } 

    return YES; 
} 

//DirectDownloadDelegate.h 
@interface DirectDownloadDelegate : NSObject { 
    NSError *error; 
    NSURLResponse * response; 
    BOOL done; 
    NSFileHandle * outputHandle; 
} 
@property (readonly, getter=isDone) BOOL done; 
@property (readonly) NSError *error; 
@property (readonly) NSURLResponse * response; 

@end 

//DirectDownloadDelegate.m 
@implementation DirectDownloadDelegate 
@synthesize error, request, done; 

- (id) initWithFilePath:(NSString *)path { 
    if (self = [super init]) { 
    if ([[NSFileManager defaultManager] fileExistsAtPath:path]) { 
     [[NSFileManager defaultManager] removeItemAtPath:path error:nil]; 
    } 
    [[NSFileManager defaultManager] createFileAtPath:path contents:nil attributes:nil]; 
    outputHandle = [[NSFileHandle fileHandleForWritingAtPath:path] retain]; 
    } 
    return self; 
} 

- (void) dealloc { 
    [error release]; 
    [response release]; 
    [outputHandle closeFile]; 
    [outputHandle release]; 
    [super dealloc]; 
} 

- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)anError { 
    error = [anError retain]; 
    [self connectionDidFinishLoading:connection]; 
} 

- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)someData { 
    [outputHandle writeData:someData]; 
} 

- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)aResponse { 
    response = [aResponse retain]; 
} 

- (void) connectionDidFinishLoading:(NSURLConnection *)connection { 
    done = YES; 
} 

La idea básica es que se crea un estándar NSURLConnection, que normalmente es asíncrona, pero sólo bloquear el hilo al girar el mismo Runloop hasta que se haga la conexión. También utiliza un delegado de conexión url personalizado para simplemente canalizar cualquier dato que la conexión reciba directamente a un archivo.

Ahora puede hacer:

NSError * downloadError = nil; 
BOOL ok = [NSURLConnection downloadItemAtURL:someURL toFile:someFile error:&downloadError]; 
if (!ok) { 
    NSLog(@"ack there was an error: %@", error); 
} else { 
    NSLog(@"file downloaded to: %@", someFile); 
} 
+1

¡Muy buena idea! Un bucle sin fin (ugh!;)) Para hacerlo sincrónico. Es una buena solución, ¡muchas gracias! También es admirable si realmente lo escribiste completamente en el navegador :-) – favo

+0

bueno, para ser honesto, tuve que hacer algo como esto la semana pasada y estaba saliendo de lo que recordaba ...;) –

+0

Siempre que tengas cuidado de que todos los que llaman están contentos de que el ciclo de ejecución principal se ejecute dentro de esa función (en particular, si está dentro de un controlador de eventos, manejará otros eventos antes de que finalice el procesamiento del evento actual. Eww.) –

Cuestiones relacionadas