【问题标题】:ios URL(string: ) not working alwaysios URL(字符串:)不总是工作
【发布时间】:2018-07-12 13:54:46
【问题描述】:
let testurl = "http://akns-
images.eonline.com/eol_images/Entire_Site/2018029/rs_1024x759-
180129200032-1024.lupita-nyongo-angela-bassett-black-panther-
premiere.ct.012918.jpg?fit=inside|900:auto"

if let url = URL(string: testurl){
    print("valid")
}else {
   print("invalid")
}

这会打印为无效的 URL。但在网络浏览器中显示图像。我见过很多方法,但它会引发 Apple 警告使用 stringByAddingPercentEncodingWithAllowedCharacters,这并不总是有效。

我更喜欢一种无需编码完整 urlstring 即可清理 url 的解决方案。在传递给外部库时,在初始步骤对其进行编码可能会产生摩擦,这将重新编码 url 并使其无效。

【问题讨论】:

标签: ios swift url nsurl url-encoding


【解决方案1】:

使用URLComponents,它会自动处理转义字符并正确编码url。无需使用addingPercentEncoding

func computeURL() {
    let testurlStr = "http://akns-images.eonline.com/eol_images/Entire_Site/2018029/rs_1024x759-180129200032-1024.lupita-nyongo-angela-bassett-black-panther-premiere.ct.012918.jpg?fit=inside|900:auto"
    let components = transformURLString(testurlStr)

    if let url = components?.url {
        print("valid")
    } else {
        print("invalid")
    }
}

func transformURLString(_ string: String) -> URLComponents? {
    guard let urlPath = string.components(separatedBy: "?").first else {
        return nil
    }
    var components = URLComponents(string: urlPath)
    if let queryString = string.components(separatedBy: "?").last {
        components?.queryItems = []
        let queryItems = queryString.components(separatedBy: "&")
        for queryItem in queryItems {
            guard let itemName = queryItem.components(separatedBy: "=").first,
                  let itemValue = queryItem.components(separatedBy: "=").last else {
                    continue
            }
            components?.queryItems?.append(URLQueryItem(name: itemName, value: itemValue))
        }
    }
    return components!
}

【讨论】:

  • 对于 testurlStr 你已经硬编码了这个值。这怎么可能是通用的解决方案?
  • 我没有查询来准备网址。我有一个带有错误字符的外部图像 url 链接,该链接在浏览器中有效,但在 URL(string: testurl) 方法中无效。我需要从中构造一个有效的 URL,而不编码完整的 url 字符串。
  • 我只是将您的代码示例作为起点。如果您只有完整的字符串,包括主机、路径和查询项,则很容易分离各个部分……我已经更新了示例
【解决方案2】:

如果字符串包含特殊字符,则 URL 编码对于从字符串构建 URL 是强制性的。

为什么我们需要 URL 编码

来自this answer

URI 表示为字符序列,而不是八位字节序列。这是因为 URI 可能通过不通过计算机网络的方式“传输”,例如打印在纸上、通过无线电读取等。

然而,对于包含非 ASCII 字符的原始字符序列,情况就比较困难了。如果可能有多个 [RFC2277],则传输旨在表示字符序列的八位字节序列的 Internet 协议有望提供某种方式来识别所使用的字符集。但是,目前通用 URI 语法中没有任何规定可以完成此标识。单个 URI 方案可能需要单个字符集、定义默认字符集或提供一种方法来指示所使用的字符集。

在 IOS SDK 中,我们有以下内容:

用户:URLUserAllowedCharacterSet
密码:URLPasswordAllowedCharacterSet
主持人:URLHostAllowedCharacterSet
路径:URLPathAllowedCharacterSet
片段:URLFragmentAllowedCharacterSet
查询:URLQueryAllowedCharacterSet

您可以使用addingPercentEncoding(withAllowedCharacters:) 正确编码字符串。

根据the apple docs for that method的一个重要说明:

不能对整个 URL 字符串进行百分比编码,因为每个 URL 组件都指定了一组不同的允许字符。例如,URL 的查询组件允许使用“@”字符,但该字符必须在密码组件中进行百分比编码。

UTF-8 编码用于确定正确的百分比编码字符。 allowedCharacters 中超出 7 位 ASCII 范围的任何字符都将被忽略。

一个例子:

let encodedURL = testurl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
if let url = URL(string: finalUrl) {
    print("valid url")
} else {
    print("invalid url")
}

我希望这会有所帮助。

【讨论】:

    【解决方案3】:

    我想我已经找到了答案。仍然会检查是否有任何副作用,欢迎使用 cmets:

    let urlst = "http://akns-
    images.eonline.com/eol_images/Entire_Site/2018029/rs_1024x759-
    180129200032-1024.lupita-nyongo-angela-bassett-black-panther-
    premiere.ct.012918.jpg?fit=inside|900:auto"
    extension String {
     func getCleanedURL() -> URL? {
        guard self.isEmpty == false else {
            return nil
        }
        if let url = URL(string: self) {
            return url
        } else {
            if let urlEscapedString = self.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) , let escapedURL = URL(string: urlEscapedString){
                return escapedURL
            }
        }
        return nil
     }
    }
    

    这不会对完整的 URL 进行编码,而只会对无效的查询字符进行编码(例如 url 中的“|”)。因此它仍然可以作为字符串或有效 URL 传递。

    【讨论】:

    • 最干净的方法是使用 URLComponents,正如我在回答中提到的那样......
    • 查看我对您的回答的评论。
    【解决方案4】:

    根据您自己的回答,我什至会进一步简化它,使其成为 URL 而不是 String 的扩展,这样我们仍然可以使用类似于 URL(string:) 的初始化程序,并具有相同的输出:

    extension URL {
        init?(_ string: String) {
            guard string.isEmpty == false else {
                return nil
            }
            if let url = URL(string: string) {
                self = url
            } else if let urlEscapedString = string.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed),
                      let escapedURL = URL(string: urlEscapedString) {
                self = escapedURL
            } else {
                return nil
            }
        }
    }
    

    【讨论】:

      【解决方案5】:

      请将您的行替换为:

      let  finalUrl = testurl.addingPercentEncoding( withAllowedCharacters: .urlUserAllowed)
      if let url = URL(string: finalUrl){
          print("valid")
      } else {
         print("invalid")
      }
      

      【讨论】:

      • @rmaddy 编辑看看
      • finalUrl是可选的,所以不能在URL(string: finalUrl)中使用。
      猜你喜欢
      • 2013-06-02
      • 1970-01-01
      • 2016-11-09
      • 2019-10-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多