Ausgabe
FTP-Download über URL-Sitzung funktioniert nicht. Ich habe versucht, den folgenden Code zu verwenden.
Ansatz 1
NSURL *url_upload = [NSURL URLWithString:@"ftp://user:[email protected]:/usr/path/file.json"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url_upload];
[request setHTTPMethod:@"PUT"];
NSString *docsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSURL *docsDirURL = [NSURL fileURLWithPath:[docsDir stringByAppendingPathComponent:@"prova.zip"]];
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfig.timeoutIntervalForRequest = 30.0;
sessionConfig.timeoutIntervalForResource = 60.0;
sessionConfig.allowsCellularAccess = YES;
sessionConfig.HTTPMaximumConnectionsPerHost = 1;
NSURLSession *upLoadSession = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];
NSURLSessionUploadTask *uploadTask = [upLoadSession uploadTaskWithRequest:request fromFile:docsDirURL];
[uploadTask resume];
Ansatz 2
NSURL *url = [NSURL URLWithString:@"ftp://121.122.0.200:/usr/path/file.json"];
NSString * utente = @"xxxx";
NSString * codice = @"xxxx";
NSURLProtectionSpace * protectionSpace = [[NSURLProtectionSpace alloc] initWithHost:url.host port:[url.port integerValue] protocol:url.scheme realm:nil authenticationMethod:nil];
NSURLCredential *cred = [NSURLCredential
credentialWithUser:utente
password:codice
persistence:NSURLCredentialPersistenceForSession];
NSURLCredentialStorage * cred_storage ;
[cred_storage setCredential:cred forProtectionSpace:protectionSpace];
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfiguration.URLCredentialStorage = cred_storage;
sessionConfiguration.allowsCellularAccess = YES;
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:nil];
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:url];
[downloadTask resume];
Der Fehler, den ich bekomme, ist wie folgt:
Die angeforderte URL wurde auf diesem Server nicht gefunden
Aber die gleiche URL funktioniert im Terminal mit dem SCP-Befehl und die Datei wird erfolgreich heruntergeladen
Lösung
Zunächst sollten Sie in Betracht ziehen, von ftp
to sftp
oder auf https
das Protokoll umzusteigen, da diese viel sicherer sind und einige andere Probleme lösen.
Abgesehen davon ist das ftp
Protokoll in iOS nicht streng verboten (im Gegensatz zu, sagen wir, http
), und Sie können es immer noch frei verwenden. Es ist jedoch NSURLSession
nicht darauf ausgelegt, standardmäßig mit FTP- Upload – Aufgaben zu arbeiten. Sie müssen also entweder einen Custom implementieren, der eine NSURLProtocol
solche Anfrage übernimmt, oder einfach andere Mittel verwenden, ohne NSURLSession
.
In beiden Fällen müssen Sie sich auf die veraltete Core Foundation API für FTP-Streams verlassen. Erstellen Sie zunächst eine CFWriteStream
, die auf die Ziel-URL auf Ihrem FTP-Server verweist, wie folgt:
CFWriteStreamRef writeStream = CFWriteStreamCreateWithFTPURL(kCFAllocatorDefault, (__bridge CFURLRef)uploadURL);
NSOutputStream *_outputStream = (__bridge_transfer NSOutputStream *)writeStream;
Geben Sie im neu erstellten Objekt den Benutzernamen und das Kennwort des Benutzers an:
[_outputStream setProperty:login forKey:(__bridge NSString *)kCFStreamPropertyFTPUserName];
[_outputStream setProperty:password forKey:(__bridge NSString *)kCFStreamPropertyFTPPassword];
Erstellen Sie als Nächstes eine NSInputStream
mit der URL zu der Quelldatei, in die Sie hochladen möchten (es ist nicht unbedingt erforderlich, den Eingabeteil an die Streams-API zu binden, aber ich finde es konsistent, da Sie sich sowieso mit Streams befassen müssen):
NSInputStream *_inputStream = [NSInputStream inputStreamWithURL:fileURL];
Jetzt der komplizierte Teil. Wenn es um Streams mit Remote-Ziel geht, müssen Sie asynchron mit ihnen arbeiten, aber dieser Teil der API ist uralt, sodass er nie Blöcke und andere praktische Funktionen des modernen Foundation
Frameworks übernommen hat. Stattdessen müssen Sie den Stream in a planen NSRunLoop
und warten, bis er den gewünschten Status an das delegate
Objekt des Streams meldet:
_outputStream.delegate = self;
NSRunLoop *loop = NSRunLoop.currentRunLoop;
[_outputStream scheduleInRunLoop:loop forMode:NSDefaultRunLoopMode];
[_outputStream open];
stream:handleEvent:
Nun wird das Delegate-Objekt über die Methode über alle Änderungen im Status des Streams benachrichtigt . Sie sollten die folgenden Status verfolgen:
NSStreamEventOpenCompleted
– Der Ausgangsstrom hat gerade eine Verbindung mit dem Zielpunkt hergestellt. Hier können Sie den Eingabestrom öffnen oder einige andere Vorbereitungen treffen, die kurz vor dem Schreiben der Daten auf den FTP-Server relevant wurden;NSStreamEventHasSpaceAvailable
– Der Ausgabestrom ist bereit, die Daten zu empfangen. Hier schreiben Sie die Daten tatsächlich an das Ziel;NSStreamEventErrorOccurred
– jede Art von Fehler, der während der Datenübertragung / Verbindung auftreten kann. Hier sollten Sie die Verarbeitung der Daten stoppen.
Beachten Sie, dass Sie nicht eine ganze Datei auf einmal hochladen möchten, erstens, weil es leicht zu einem Speicherüberlauf auf einem mobilen Gerät kommen kann, und zweitens, weil die entfernte Datei möglicherweise nicht jedes gesendete Byte sofort verbraucht. In meiner Implementierung sende ich die Daten mit Blöcken von 32 KB:
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
switch (eventCode) {
case NSStreamEventOpenCompleted:
[_inputStream open];
return;
case NSStreamEventHasSpaceAvailable:
if (_dataBufferOffset == _dataBufferLimit) {
NSInteger bytesRead = [_inputStream read:_dataBuffer maxLength:kDataBufferSize];
switch (bytesRead) {
case -1:
[self p_cancelWithError:_inputStream.streamError];
return;
case 0:
[aStream removeFromRunLoop:NSRunLoop.currentRunLoop forMode:NSDefaultRunLoopMode];
// The work is done
return;
default:
_dataBufferOffset = 0;
_dataBufferLimit = bytesRead;
}
}
if (_dataBufferOffset != _dataBufferLimit) {
NSInteger bytesWritten = [_outputStream write:&_dataBuffer[_dataBufferOffset]
maxLength:_dataBufferLimit - _dataBufferOffset];
if (bytesWritten == -1) {
[self p_cancelWithError:_outputStream.streamError];
return;
} else {
self.dataBufferOffset += bytesWritten;
}
}
return;
case NSStreamEventErrorOccurred:
[self p_cancelWithError:_outputStream.streamError];
return;
default:
break;
}
}
An der Zeile mit // The work is done
Kommentar gilt die Datei als vollständig hochgeladen.
Vorausgesetzt, wie komplex dieser Ansatz ist und dass es nicht wirklich machbar ist, alle Teile davon in eine einzige SO-Antwort zu packen, habe ich hier im Wesentlichen eine Hilfsklasse zur Verfügung gestellt . Sie können es so einfach im Client-Code verwenden:
NSURL *filePathURL = [NSBundle.mainBundle URLForResource:@"895971" withExtension:@"png"];
NSURL *uploadURL = [[NSURL URLWithString:@"ftp://ftp.dlptest.com"] URLByAppendingPathComponent:filePathURL.lastPathComponent];
TDWFTPUploader *uploader = [[TDWFTPUploader alloc] initWithFileURL:filePathURL
uploadURL:uploadURL
userLogin:@"dlpuser"
userPassword:@"rNrKYTX9g7z3RgJRmxWuGHbeu"];
[uploader resumeWithCallback:^(NSError *_Nullable error) {
if (error) {
NSLog(@"Error: %@", error);
} else {
NSLog(@"File uploaded successfully");
}
}];
Es muss nicht einmal beibehalten werden, da die Klasse einen Thread erzeugt, der die Instanz behält, bis die Arbeit erledigt ist. Ich habe den Ausnahmefällen nicht allzu viel Aufmerksamkeit geschenkt, also lassen Sie es mich gerne wissen, wenn es einige Fehler enthält oder das erforderliche Verhalten nicht erfüllt.
BEARBEITEN
Bei GET
Anfragen besteht der einzige Unterschied zu anderen Protokollen darin, dass Sie Login und Passwort als Teil der URL übergeben und keine sicheren Mittel verwenden können, um dasselbe zu tun. Abgesehen davon funktioniert es unkompliziert:
NSURLComponents *components = [NSURLComponents componentsWithString:@"ftp://121.122.0.200"];
components.path = @"/usr/path/file.json";
components.user = @"user";
components.password = @"pwd";
[[NSURLSession.sharedSession dataTaskWithURL:[components URL] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable
response, NSError * _Nullable error) {
NSLog(@"%@", response);
}] resume];
Beantwortet von – The Dreams Wind
Antwort geprüft von – Pedro (FixError Volunteer)