2016-03-15 50 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?