如何通过继承NSURLProtocol来成功处理sftp://协议?

NSURLConnection支持的内置URL协议可以处理方案http,https,文件,ftp,about和数据 。 我想支持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://username@192.168.0.1: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
}
}

增加对URL方案本身的支持不会增加对底层网络协议的支持。 sftp协议与HTTP无关,并且需要完全不同的网络代码才能进行连接和下载数据。 现在,您的自定义协议类基本上只是要求URL加载系统在协议获取sftp URL(或任何其他URL)时创建新的sftp请求。 这将始终失败,因为URL加载系统不知道如何处理sftp请求。

要添加sftp支持,您需要引入一个实际的sftp库,然后使用它来代替在startLoading方法中创建新的NSURLConnection 。 您还需要检查canInitWithRequest的协议,以确保它确实是sftp请求,IIRC。 否则,您的自定义协议子类将最终处理所有可能的URL方案的所有请求。

随着中说,除非有一个很好的理由使用处理SFTP NSURLConnectionNSURLSession ,你可能会更好过只是处理,通过使用这些SFTP库的一个直接,而不是试图将其下探到URL装载系统。

有关sftp库的信息,请参阅此问题:

用于iPhone的SFTP库?

链接地址: http://www.djcxy.com/p/34847.html

上一篇: How to successfully handle sftp:// protocol by subclassing NSURLProtocol?

下一篇: Video streaming in MPMoviePlayerController embedded in UIWebView