2016-03-15 49 views
1

NSURLConnection支持的內置URL協議可以處理http,https,file,ftp,about和數據的方案。我想支持sftp。我聽說有一種方法可以通過繼承NSURLProtocol來實現此目的。但我沒有得到如何去做。我想通過sftp從文件夾下載圖像。如何通過繼承NSURLProtocol來成功處理sftp://協議?

來源:https://www.raywenderlich.com/76735/using-nsurlprotocol-swift

的教程說的子類,我們可以支持自定義URL。但是當我運行代碼時,連接總是失敗。我想當我們嘗試連接到sftp,委託方法在MyURLProtocol.swiftdidReceiveAuthenticationChallenge會被調用,但這並沒有發生。相反,代理方法didFailWithError被調用。我不明白爲什麼連接失敗。這兩種方法都來自NSURLConnectionDelegate

我有一個ViewController.swift

override func viewDidLoad() { 
    super.viewDidLoad() 
    // Do any additional setup after loading the view, typically from a nib. 
    let urlString = "sftp://[email protected]:22/batman" 
    // Open a connection for the URL. 
    var url = NSURL(string: urlString) 
    request = NSURLRequest(URL: url!) 
    connection = NSURLConnection(request: request, delegate: self, startImmediately: true)//(request: request, delegate: self) 

} 

在我AppDelegate.swift

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 
    // Override point for customization after application launch. 
    NSURLProtocol.registerClass(MyURLProtocol) 
    return true 
} 

MyURLProtocol.swift

import UIKit 
import CoreData 

var requestCount = 0 

class MyURLProtocol: NSURLProtocol, NSURLConnectionDelegate { 
var connection: NSURLConnection! 
var mutableData: NSMutableData! 
var response: NSURLResponse! 

override class func canInitWithRequest(request: NSURLRequest) -> Bool { 
    print("Request #\(requestCount++): URL = \(request.URL!.absoluteString)") 
    if NSURLProtocol.propertyForKey("MyURLProtocolHandledKey", inRequest: request) != nil { 
     return false 
    } 
    return true 
} 

override class func canonicalRequestForRequest(request: NSURLRequest) -> NSURLRequest { 
    return request 
} 

override class func requestIsCacheEquivalent(aRequest: NSURLRequest, 
    toRequest bRequest: NSURLRequest) -> Bool { 
     return super.requestIsCacheEquivalent(aRequest, toRequest:bRequest) 
} 

override func startLoading() { 
    // 1 
    let possibleCachedResponse = self.cachedResponseForCurrentRequest() 
    if let cachedResponse = possibleCachedResponse { 
     print("Serving response from cache") 

     // 2 
     let data = cachedResponse.valueForKey("data") as! NSData 
     let mimeType = cachedResponse.valueForKey("mimeType") as! String 
     let encoding = cachedResponse.valueForKey("encoding") as! String 

     // 3 
     let response = NSURLResponse(URL: self.request.URL!, MIMEType: mimeType, expectedContentLength: data.length, textEncodingName: encoding) 

     // 4 
     self.client!.URLProtocol(self, didReceiveResponse: response, cacheStoragePolicy: .NotAllowed) 
     self.client!.URLProtocol(self, didLoadData: data) 
     self.client!.URLProtocolDidFinishLoading(self) 
    } else { 
     // 5 
     print("Serving response from NSURLConnection") 

     let newRequest = self.request.mutableCopy() as! NSMutableURLRequest 
     NSURLProtocol.setProperty(true, forKey: "MyURLProtocolHandledKey", inRequest: newRequest) 
     self.connection = NSURLConnection(request: newRequest, delegate: self) 
    } 
} 

override func stopLoading() { 
    if self.connection != nil { 
     self.connection.cancel() 
    } 
    self.connection = nil 
} 

func connection(connection: NSURLConnection!, didReceiveResponse response: NSURLResponse!) { 
    self.client!.URLProtocol(self, didReceiveResponse: response, cacheStoragePolicy: .NotAllowed) 

    self.response = response 
    self.mutableData = NSMutableData() 
} 

func connection(connection: NSURLConnection!, didReceiveData data: NSData!) { 
    self.client!.URLProtocol(self, didLoadData: data) 
    self.mutableData.appendData(data) 
} 

func connectionDidFinishLoading(connection: NSURLConnection!) { 
    self.client!.URLProtocolDidFinishLoading(self) 
    self.saveCachedResponse() 
} 

func connection(connection: NSURLConnection, didFailWithError error: NSError) { 
    self.client!.URLProtocol(self, didFailWithError: error) 
} 

func connection(connection: NSURLConnection, didReceiveAuthenticationChallenge challenge: NSURLAuthenticationChallenge) { 
} 

func saveCachedResponse() { 
    print("Saving cached response") 

    // 1 
    let delegate = UIApplication.sharedApplication().delegate as! AppDelegate 
    let context = delegate.managedObjectContext 

    // 2 
    let cachedResponse = NSEntityDescription.insertNewObjectForEntityForName("CachedURLResponse", inManagedObjectContext: context) as NSManagedObject 

    cachedResponse.setValue(self.mutableData, forKey: "data") 
    cachedResponse.setValue(self.request.URL!.absoluteString, forKey: "url") 
    cachedResponse.setValue(NSDate(), forKey: "timestamp") 
    cachedResponse.setValue(self.response.MIMEType, forKey: "mimeType") 
    cachedResponse.setValue(self.response.textEncodingName, forKey: "encoding") 

    // 3 
    do { 
     try context.save() 
    } catch let error as NSError { 
     print(error) 
     print("Could not cache the response") 
    } 
} 

func cachedResponseForCurrentRequest() -> NSManagedObject? { 
    // 1 
    let delegate = UIApplication.sharedApplication().delegate as! AppDelegate 
    let context = delegate.managedObjectContext 

    // 2 
    let fetchRequest = NSFetchRequest() 
    let entity = NSEntityDescription.entityForName("CachedURLResponse", inManagedObjectContext: context) 
    fetchRequest.entity = entity 

    // 3 
    let predicate = NSPredicate(format:"url == %@", self.request.URL!.absoluteString) 
    fetchRequest.predicate = predicate 

    // 4 
    let possibleResult:Array<NSManagedObject>? 
    do { 
     possibleResult = try context.executeFetchRequest(fetchRequest) as? Array<NSManagedObject> 
     if let result = possibleResult { 
      if !result.isEmpty { 
       return result[0] 
      } 
     } 
    } catch let error as NSError { 
     print(error) 
    } 
    return nil 
} 
} 
+0

不caninitWithRequest曾經被調用? –

+0

是的,它被調用。 –

回答

1

添加對URL方案本身的支持不會增加對底層網絡協議的支持。 sftp協議與HTTP無關,並且需要完全不同的網絡代碼才能進行連接和下載數據。現在,您的自定義協議類基本上只是要求URL加載系統在您的協議獲取sftp URL(或任何其他URL)時創建新的sftp請求。這將始終失敗,因爲URL加載系統不知道如何處理sftp請求。

要添加SFTP支持,你需要在實際SFTP庫帶來,然後使用您startLoading方法創建一個新的NSURLConnection的那個來代替。您還需要檢查canInitWithRequest中的協議以確保它確實是sftp請求IIRC。否則,您的自定義協議子類將最終處理所有可能的URL方案的所有請求。

這樣說的話,除非有很好的理由使用NSURLConnectionNSURLSession來處理sftp,否則你最好直接使用其中一個sftp庫來處理,而不是試圖將它們放到URL加載中系統。

有關SFTP庫的信息,請參閱此問題:

SFTP libraries for iPhone?