【问题标题】:How to successfully handle sftp:// protocol by subclassing NSURLProtocol?如何通过继承 NSURLProtocol 成功处理 sftp:// 协议?
【发布时间】:2016-06-30 10:04:55
【问题描述】:

NSURLConnection 支持的内置 URL 协议可以处理 http、https、file、ftp、about 和 data 方案。我想支持 sftp。我听说有一种方法可以通过继承 NSURLProtocol 来实现。但我不知道该怎么做。我想通过sftp从文件夹中下载一张图片。

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

教程说通过子类化我们可以支持自定义 URL。但是当我运行代码时,连接总是失败。我想当我们尝试连接到 sftp 时,MyURLProtocol.swift 中的委托方法,即 didReceiveAuthenticationChallenge 会被调用,但这不会发生。而是调用委托方法 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
}
}

【问题讨论】:

  • caninitWithRequest 曾经被调用过吗?

标签: ios swift nsurlconnection nsurl nsurlrequest


【解决方案1】:

添加对 URL 方案本身的支持不会添加对底层网络协议的支持。 sftp 协议与 HTTP 无关,需要完全不同的网络代码来建立连接和下载数据。现在,您的自定义协议类基本上只是在您的协议获得 sftp URL(或任何其他 URL)时要求 URL 加载系统发出新的 sftp 请求。这总是会失败,因为 URL 加载系统不知道如何处理 sftp 请求。

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

话虽如此,除非有充分的理由使用 NSURLConnectionNSURLSession 处理 sftp,否则您最好直接使用其中一个 sftp 库来处理它,而不是尝试将它们插入URL 加载系统。

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

SFTP libraries for iPhone?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多