L'NSURLDownload proporciona a una aplicació l'habilitat de descarregar els continguts d'una URL directament al disc. Proporciona una interfície semblant a l'NSURLConnection, afegint un mètode addicional per especificar el destí del fitxer. L'NSURLDownload també pot decodificar els esquemes codificats més utilitzats com són MacBinary, BinHex i gzip. A diferència de l'NSURLConnection, les dades descarregades utilitzant un NSURLDownload no s'emmagatzema en el sistema de memòria cau.
| Nota: Si la teva aplicació no està restringida utilitzant classes Foundation, la framework Web Kit inclou WebDownload, una sub-classe de NSURLDownload que proporciona una interfície d'usuari per l'autenticació. |
Un patró d'utilització per NSURLDownload és descarregar un fitxer a un fitxer predeterminat en el disc. Si l'aplicació coneix el destí de la descàrrega, pot explícitament posar-lo utilitzant el setDestination:allowOverwrite:. Múltiples missatges setDestination:allowOverwrite: a una instància NSURLDownload s'ignoren.
La descàrrega comença immediàtament al rebre el missatge initWithRequest:delegate:. Pot cancel·lar-se en qualsevol moment abans que el delegat rebi un missatge downloadDidFinish: o download:didFailWithError: enviant a la descàrrega un missatge cancel.
L'exemple del Llistat 1 activa el destí, i això requereix que el delegat només implementi els mètodes download:didFailWithError: i downloadDidFinish:.
Llistat 1: Utilitzant NSURLDownload amb un fitxer destí predeterminat
- (void)startDownloadingURL:sender
{
// crea la petició
NSURLRequest *laPeticio=[NSURLRequest requestWithURL:[NSURL URLWithString:CONFIG_SOURCE_URL_STRING]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// crea la connexió amb la petició
// i comença carregant les dades
laDescarrega = [[NSURLDownload alloc] initWithRequest:laPeticio
delegate:self];
if (laDescarrega) {
// activa la destinació del fitxer
[laDescarrega setDestination:CONFIG_SOURCE_PATH allowOverwrite:YES];
} else {
// informa a l'usuari que la descàrrega no pot fer-se
}
}
- (void)download:(NSURLDownload *)descarrega didFailWithError:(NSError *)error
{
// allibera la connexió
[descarrega release];
// informa a l'usuari
NSLog(@"Descàrrega fallida! Error - %@ %@",
[error localizedDescription],
[[error userInfo] objectForKey:NSErrorFailingURLStringKey]);
}
- (void)downloadDidFinish:(NSURLDownload *) descarrega
{
// allibera la connexió
[descarrega release];
// fer quelcom amb les dades
NSLog(@"laDescàrregaAFinalitzat");
}
Altres mètodes poden implementar-se per a delegar tractament personalitzats d'autenticació, redireccions de servidor i decodificació de fitxers.
Un altre situació comuna és que l'aplicació hauria de derivar el fitxer destí des de les dades descarregades a ells mateixos. Això requereix que implementis el mètode delegat download:decideDestinationWithSuggestedFilename: i crida al setDestination:allowOverwrite: amb el fitxer suggerit. L'exemple del Llistat 2 guarda el fitxer descarregat a l'escriptori de l'usuari utilitzant un fitxer suggerit.
Llistat 2: Utilitzant NSURLDownload amb un nom de fitxer derivar de la descàrrega
- (void)startDownloadingURL:sender
{
// crea la petició
NSURLRequest *laPeticio=[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.apple.com/index.html"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// crea la connexió amb la petició
// i comença la càrrega de dades
laDescarrega=[[NSURLDownload alloc] initWithRequest: laPeticio delegate:self];
if (! laDescarrega) {
// informa a l'usuari que la descàrrega podria no haver-se fet
}
}
- (void)download:(NSURLDownload *)descarrega decideDestinationWithSuggestedFilename:(NSString *)nomFitxer
{
NSString *nomFitxerDesti;
NSString *directoriPersonal=NSHomeDirectory();
nomFitxerDesti =[[directoriPersonal stringByAppendingPathComponent:@"Desktop"]
stringByAppendingPathComponent: nomFitxer];
[descarrega setDestination: nomFitxerDesti allowOverwrite:NO];
}
- (void)download:(NSURLDownload *)descarrega didFailWithError:(NSError *)error
{
// allibera la connexió
[descarrega release];
// informa l'usuari
NSLog(@"Descàrrega fallida! Error - %@ %@",
[error localizedDescription],
[[error userInfo] objectForKey:NSErrorFailingURLStringKey]);
}
- (void)downloadDidFinish:(NSURLDownload *) descarrega
{
// allibera la connexió
[descarrega release];
// fer quelcom amb les dades
NSLog(@"laDescàrregaAFinalitzat");
}
El fitxer descarregat s'ha emmagatzemat en l'escriptori de l'usuari amb el nom index.html, que s'ha derivat des del contingut descarregat. Passant un NO a setDestination:allowOverwrite: prevé que un fitxer existent sigui sobre-escrit per la descàrrega. En comptes d'un únic nom de fitxer es creat afegint un nombre seqüencial després del nom del fitxer, index-1.html.
El delegat es informat quan un fitxer es creat al disc si aquest implementa el mètode download:didCreateDestination:. Aquest mètode també dona a l'aplicació l'oportunitat de determinar el nom de fitxer final amb que la descàrrega s'ha guardat.
L'exemple del Llistat 3 registra del nom del fitxer final.
Llistat 3: Registrant el nom del fitxer final utilitzant download:didCreateDestination:
-(void)download:(NSURLDownload *)descarrega didCreateDestination:(NSString *)path
{
// path ara conté el camí destí
// de la descàrrega, agafant dins del comte qualsevol
// nom únic causat per -setDestination:allowOverwrite:
NSLog(@â€?Destinació final del fitxer: %@â€?,path);
}
Aquest missatge s'envia al delegat després que s'hagi donat una oportunitat per respondre als missatges download:shouldDecodeSourceDataOfMIMEType: i download:decideDestinationWithSuggestedFilename:.
El progrés de la descàrrega pot ser determinada implementant els mètodes delegats download:didReceiveResponse: i download:didReceiveDataOfLength:.
El mètode download:didReceiveResponse: proporciona al delegat una oportunitat per determinar la longitud del contingut esperat de l'NSURLResponse. El delegat hauria de ressetejar el progrés cada cop que es rebi aquest missatge.
L'exemple d'implementació del Llistat 4 demostra utilitzant aquests mètodes com proporcionar realimentació de progrés a l'usuari.
Llistat 4: Mostrant el progrés de la descàrrega
- (void)setRespostaDeDescarrega:(NSURLResponse *)unaRespostaDeDescarrega
{
[unaRespostaDeDescarrega retain];
[unaRespostaDeDescarrega release];
repostaDeDescarrega = unaRespostaDeDescarrega;
}
- (void)download:(NSURLDownload *)descarrega didReceiveResponse:(NSURLResponse *)resposta
{
// reseteja el progrés, aquest pot ser cridada molts cops
bytesRebuts=0;
// reté la resposta per utilitzar-se després
[self setRespostaDeDescarrega: resposta];
}
- (void)download:(NSURLDownload *)descarrega didReceiveDataOfLength:(unsigned)longitud
{
long long longitudEsperada=[[self repostaDeDescarrega] expectedContentLength];
bytesRebuts = bytesRebuts + longitud;
if (longitudEsperada != NSURLResponseUnknownLength) {
// si la lontingut del contingut esperat està
// disponible, mostra el percentatge per completar-ho
float percentCompletar=(bytesRebuts/(float) longitudEsperada)*100.0;
NSLog(@"Percentatge per completar - %f",percentCompletar);
} else {
// si la longitud del contingut esperat és
// descneguda, només mostra les progrés
NSLog(@"Bytes rebuts - %d", bytesRebuts);
}
}
El delegat rep un missatge download:didReceiveResponse: abans que comenci a rebre els missatges download:didReceiveDataOfLength:.
No és infreqüent que un servidor redireccioni una URL a una altra. El delegat de l'NSURLConnection rebrà un missatge connection:willSendRequest:redirectResponse: quan això passi.
Si el delegat implementa aquest mètode pot examinar la nova NSURLRequest i la NSURLResponse que ha produït la redirecció i permetre la petició NSURLRequest s'utilitzi per la connexió, crear una nova NSURLRequest per la connexió, refusar la redirecció i agafar el retorn de la redirecció amb les dades rebudes des de la NSURLResponse que ha produït la redirecció, o cancel·lar completament la descàrrega.
Per permetre la redirecció, el delegat hauria de retornar la NSURLRequest proporcionada. El delegat també podria crear un nou NSURLRequest, apuntant a la nova URL, i retornant-lo.
Si el delegat desitja refusar la redirecció, però rep alguna dada de la connexió, el mètode hauria de retornar nil.
Finalment, el delegat pot cancel·lar la redirecció i la connexió, enviant un missatge cancel a la connection.
El delegat també rebrà aquest missatge si la sub-classe de NSURLProtocol que tracta la petició que ha canviat la petició NSURLRequest per estandaritzar aquest format, per exemple, canviant una petició de "http://www.apple.com" < "http://www.apple.com/". Aquest és requerit doncs la versió estandaritzada o canònica de la petició s'utilitza per la gestió de la memòria cau. En aquesta cas especial, la resposta passada al delegat és nil i el delegat hauria simplement retornar la NSURLRequest proporcionada.
L'exemple d'implementació del Llistat 6 permet canvis canònics i denega totes les redireccions del servidor.
Llistat 5: Exemple d'implementació de connection:willSendRequest:redirectResponse:
-(NSURLRequest *)connection:(NSURLConnection *)conexio
willSendRequest:(NSURLRequest *)peticio
redirectResponse:(NSURLResponse *) respostaRedireccio
{
NSURLRequest * novaPeticio = peticio;
if (respostaRedireccio) {
newRequest = nil;
}
return novaPeticio;
}
Si el delegat no implementa el connection:willSendRequest:redirectResponse:, tots els canvis canònics i redireccions del servidor es permetran.
Si una petició requereix autenticació i no hi ha disponibles credencials vàlides, qualsevol part de la URL requerida o en la NSURLCredentialStorage compartida, els delegats de NSURLConnection rep un missatge connection:didReceiveAuthenticationChallenge:. Per que la connexió continuï, el delegat ha de proporcionar les credencials per intentar utilitzar com a autenticació, intentar continuar sense credencials, o cancel·lar la petició d'autenticació.
La instància NSURLAuthenticationChallenge passada al delegat conté informació sobre que ha activar el desafiament d'autenticació, el nombre d'intents que s'han realitzat, qualsevol credencial provada, la NSURLProtocolSpace que requereix les credencials, i l'enviador del desafiament.
Sovint el delegat demana a l'usuari que entri un nom d'usuari i paraula de pas vàlids. Si el desafiament d'autenticació ha intentat autenticar i ha fallat, les credencials provades seran retornades enviant challenge a un missatge proposedCredential. El delegat llavors pot utilitzar aquestes credencials per publicar un diàleg que el presenta a l'usuari.
Invocant previousFailureCount en el paràmetre challenge retorna el nombre d'intents d'autenticació. El delegat pot proporcionar aquesta informació a l'usuari final, determinar si les credencials donades anteriorment han fallat, o el limitar el nombre màxim d'intents d'autenticació.
A l'intentar autenticar, l'aplicació hauria de crear un objecte NSURLCredential amb el nom de l'usuari, la paraula de pas i el tipus de persistència a utilitzar per les credencials, i llavors enviar a l'[challenge sender] un missatge useCredential:forAuthenticatioinChallenge:.
Si el delegat escull no proporcionar una credencial per el desafiament d'autenticació, podrà intentar continuar sense aquest enviant al [challenge sender] un missatge continueWithoutCredentialsForAuthenticationChallenge:. Depenent de la implementació del protocol, aquest pot retornar un contingut d'una URL alternativa que no requereix autenticació o provocar que la connexió falli, rebent un missatge connectionDidFailWithError:.
El delegat també pot escollir cancel·lar el desafiament d'autenticació enviant un missatge cancelAuthenticationChallenge: al [challenge sender]. El delegat rep un missatge connection: didCancelAuthenticationChallenge: proporcionant-li l'oportunitat per dona realimentació a l'usuari.
L'exemple del Llistat 6 intenta autenticar el desafiament creant una instància d'NSURLCredential utilitzant un nom d'usuari i una paraula de pas proporcionada per les preferències de l'aplicació. Si l'autenticació ha fallat anteriorment, aquest cancel·la el desafiament d'autenticació i informa a l'usuari.
Llistat 6: Exemple del mètode delegat connection: didReceiveAuthenticationChallenge:
-(void)connection:(NSURLConnection *)conexio
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)desafiament
{
if ([desafiament previousFailureCount] == 0) {
NSURLCredential *novaCredencial;
novaCredencial =[NSURLCredential credentialWithUser:[self preferencesName]
password:[self preferencesPassword]
persistence:NSURLCredentialPersistenceNone];
[[desafiament sender] useCredential: novaCredencial
forAuthenticationChallenge: desafiament];
} else {
[[desafiament sender] cancelAuthenticationChallenge: desafiament];
// informa a l'usuari que el nom d'usuari i la paraula de pas
// de les preferències són incorrectes
[self showPreferencesCredentialsAreIncorrectPanel:self];
}
}
L'NSURLDownload proporciona suport per decodificar els següents formats de fitxer: MacBinary, BinHex i gzip. Si l'NSURLDownload determina que un fitxer està codificat en un format suportat, intenta enviar al delegat un missatge download:shouldDecodeSourceDataOfMIMEType:. Si el delegat implementa aquest mètode, podria examinar el tipus MIME passat i retornar YES si el fitxer podria ser decodificat.
L'exemple del Llistat 7 compara els tipus MIME del fitxer i permet decodificar el contingut codificat en MacBinary i BinHex.
Llistat 7: Exemple d'implementació del mètode download:shouldDecodeSourceDataOfMIMEType:
- (BOOL)download:(NSURLDownload *)descarrega
shouldDecodeSourceDataOfMIMEType:(NSString *)tipusCodificacio;
{
BOOL haDeDescodificar=NO;
if ([tipusCodificacio isEqual:@"application/macbinary"]) {
haDeDescodificar =YES;
} else if ([tipusCodificacio isEqual:@"application/binhex"]) {
haDeDescodificar =YES;
} else if ([tipusCodificacio isEqual:@"application/gzip"]) {
haDeDescodificar =NO;
}
return haDeDescodificar;
}