使用 URL 會話 FTP 下載不起作用。我嘗試使用下面的代碼。
方法一
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];
方法二
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];
我得到的錯誤如下:
在此服務器上找不到請求的 url
但是相同的 url 正在使用 SCP 命令在終端中作業,并且檔案正在成功下載
uj5u.com熱心網友回復:
首先,您應該考慮從ftptosftp或https協議切換,因為它們更安全并且可以解決一些其他問題。
話雖如此,ftpiOS 中并沒有嚴格禁止協議(不像,比如說,http),你仍然可以自由地使用它。然而NSURLSession,它并不是為開箱即用的 ftp上傳任務而設計的。因此,您要么必須實作NSURLProtocol采用此類請求的自定義,要么僅使用其他方式而不使用NSURLSession.
無論哪種方式,您都必須依賴已棄用的 Core Foundation API 來處理 FTP 流。首先在您的 ftp-server 上創建一個CFWriteStream指向目標 url 的檔案,如下所示:
CFWriteStreamRef writeStream = CFWriteStreamCreateWithFTPURL(kCFAllocatorDefault, (__bridge CFURLRef)uploadURL);
NSOutputStream *_outputStream = (__bridge_transfer NSOutputStream *)writeStream;
并在新創建的物件中指定用戶的登錄名和密碼:
[_outputStream setProperty:login forKey:(__bridge NSString *)kCFStreamPropertyFTPUserName];
[_outputStream setProperty:password forKey:(__bridge NSString *)kCFStreamPropertyFTPPassword];
NSInputStream接下來,使用要上傳到的源檔案的 URL創建一個(不必將輸入部分系結到流 API,但我發現它是一致的,因為無論如何您都必須處理流):
NSInputStream *_inputStream = [NSInputStream inputStreamWithURL:fileURL];
現在是復雜的部分。當涉及到具有遠程目標的流時,您必須異步處理它們,但這部分 API 已經過時了,因此它從未采用現代Foundation框架的任何塊和其他方便的特性。相反,您必須在 a 中安排流NSRunLoop并等待它向delegate流的物件報告所需的狀態:
_outputStream.delegate = self;
NSRunLoop *loop = NSRunLoop.currentRunLoop;
[_outputStream scheduleInRunLoop:loop forMode:NSDefaultRunLoopMode];
[_outputStream open];
現在,將通過該stream:handleEvent:方法通知委托物件有關流狀態的任何更新。您應該跟蹤以下狀態:
NSStreamEventOpenCompleted- 輸出流剛剛與目標點建立連接。在將資料寫入 ftp 服務器之前,您可以在此處打開輸入流或做一些其他相關的準備作業;NSStreamEventHasSpaceAvailable- 輸出流已準備好接收資料。這是您實際將資料寫入目的地的位置;NSStreamEventErrorOccurred- 在資料轉換/連接期間可能發生的任何型別的錯誤。在這里您應該停止處理資料。
請注意,您不想一次性上傳整個檔案,首先因為您可能很容易在移動設備中導致記憶體溢位,其次因為遠程檔案可能不會立即消耗所有發送的位元組。在我的實作中,我使用 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;
}
}
在帶有// The work is done注釋的行中,檔案被認為已完全上傳。
假設這種方法有多復雜,并且將它的所有部分都放在一個 SO 答案中并不可行,我在這里的要點中提供了一個輔助類。您可以在客戶端代碼中使用它,就像這樣簡單:
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");
}
}];
它甚至不需要保留,因為該類會產生一個執行緒,該執行緒會保留實體直到作業完成。我沒有過多關注任何極端情況,因此請隨時讓我知道它是否有一些錯誤或不符合要求的行為。
編輯
對于GET請求,與任何其他協議的唯一區別是您將登錄名和密碼作為 URL 的一部分傳遞,并且不能使用任何安全手段來做同樣的事情。除此之外,它的作業原理很簡單:
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];
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/513020.html
標籤:IOS目标-cftp
下一篇:SwifttakeRetainedValue()和takeUnretainedValue()都是EXC_BAD_ACCESS
