【问题标题】:What's the "internal method" of R's download.file?R的download.file的“内部方法”是什么?
【发布时间】:2015-06-29 19:21:09
【问题描述】:

我正在尝试使用download.file 下载以下数据集,该数据集仅在method = "wget" 时有效)

# Doesn't work
download.file('http://uofi.box.com/shared/static/bba3968d7c3397c024ec.dta', tempfile(), method = "auto")
download.file('http://uofi.box.com/shared/static/bba3968d7c3397c024ec.dta', tempfile(), method = "curl")

# Works
download.file('http://uofi.box.com/shared/static/bba3968d7c3397c024ec.dta', tempfile(), method = "wget")

根据help(download.file)

如果选择了method = "auto"(默认),内部方法是 为 file:// URL 和其他提供的 URL 选择 能力(“http/ftp”)是真的(几乎总是这样)。

看源码,“内部方法”指的是:

if (method == "internal") {
        status <- .External(C_download, url, destfile, quiet, 
            mode, cacheOK)
        if (!quiet) 
            flush.console()
    }

但是,我仍然不知道.External(C_download) 做了什么,尤其是跨平台。知道这一点对我来说很重要,而不是依赖 wget,因为我正在编写一个可以跨平台工作的包。

【问题讨论】:

  • 我认为这是指为您的操作系统配置的方法。例如,它可能是curl
  • 看函数定义。非常清楚当您调用不同的 method 值时发生了什么。
  • @RomanLuštrik 感谢您指出来源。很明显method = "auto" 调用了.External(C_download),但此时我又被难住了。如何知道这个外部函数是做什么的? (找到.Internal().Primitive() 的C 代码会容易得多)

标签: r download


【解决方案1】:

源代码在 R 源代码中(从http://cran.r-project.org/sources.html 下载当前版本)。相关代码(从 R 3.2.1 开始)位于“./src/modules/internet/internet.c”和“./src/modules/internet/nanohttp.c”中。

根据后者,极简HTTP GET功能的代码是基于libxml2-2.3.6的。

如果您不想下载整个 .tgz 文件并解压缩,这些文件也可以在 R svn 站点 https://svn.r-project.org/R/branches/R-3-2-branch/src/modules/internet/internet.chttps://svn.r-project.org/R/branches/R-3-2-branch/src/modules/internet/nanohttp.c 上找到。

如果您查看代码,其中大部分是跨平台的。但是,在 Windows 上,似乎使用了 wininet 代码。

最初通过查看 utils 包来识别代码,因为这是 R 命令 download.file 所在的位置。我grepped在“./src/library/utils/src”目录下的c文件中下载,发现相关代码在“sock.c”中。该文件中有一条评论,上面写着/* from src/main/internet.c */,所以我接下来去了“internet.c”。

关于您的特定文件,问题在于您拥有的链接返回302 Found 状态代码。在 Windows 上并使用 wget,下载例程遵循 302 响应的 Location 字段并获取实际文件。使用 curl 方法有效,但前提是您提供参数 extra="-L"

download.file('http://uofi.box.com/shared/static/bba3968d7c3397c024ec.dta', tempfile(), method = "curl", extra="-L")

有一个名为downloader 的包声称为https 提供了一个很好的跨平台解决方案。给定一个 http URL,它只是将调用传递给download.file。这是一个也适用于 http 的版本。它还默认为二进制传输,这通常看起来是个好主意。

my_download <- function(url, destfile, method, quiet = FALSE,
                        mode = "wb", cacheOK = TRUE, extra = getOption("download.file.extra")) {
  if (.Platform$OS.type == "windows" && (missing(method) || method %in% c("auto", "internal", "wininet"))) {
    seti2 <- utils::"setInternet2"
    internet2_start <- seti2(NA)
    on.exit(suppressWarnings(seti2(internet2_start)))
    suppressWarnings(seti2(TRUE))
  } else {
    if (missing(method)) {
      if (nzchar(Sys.which("wget")[1])) {
        method <- "wget"
      } else if (nzchar(Sys.which("curl")[1])) {
        method <- "curl"
        if (!grepl("-L", extra)) {
          extra <- paste("-L", extra)
        }
      } else if (nzchar(Sys.which("lynx")[1])) {
        method <- "lynx"
      } else {
        stop("no download method found")
      }
    }
  }
  download.file(url = url, destfile = destfile, method = method, quiet = quiet, mode = mode,
                cacheOK = cacheOK, extra = extra)
}

【讨论】:

  • 您能否解释一下您是如何设法将其追溯到此来源的(尤其是因为这是一个.External() 电话)?鉴于这是内部方法的工作原理,为什么它不适用于我的问题中的文件?
  • @Heisenberg 在上面添加了这个,以及一个跨平台功能,应该遵循 Windows、Mac 和 Linux 上的 302 重定向。基于downloader::download
  • 非常详细的答案!所以我由此推断 1)“内部”方法不适用于这种情况,因为链接更改为 https,也许是 Box.com 的功能? 2)你建议的解决方案是使用downloader包还是自定义下载功能?
  • @Heisenberg 不完全是。 Linux 上的内部方法根本不处理 302 重定向。没有 -L 参数的 curl 也不会。 downloader 包什么都不做,除非有一个 https URL。我提供的功能应该(理论上)适用于所有三个平台上提供的 URL,但我还没有在 Mac OS X 上测试
  • 对不起,我不应该说对downloader::download 无能为力;我的意思是它只是将请求原封不动地传递给download.file
【解决方案2】:

您可以自己回答这个问题。只需在控制台提示符下输入download.file,您应该会在函数定义顶部附近看到它:

if (method == "auto") {   # this is actually the default from
                          # getOption("download.file.method", default = "auto")

        if (capabilities("http/ftp")) 
            method <- "internal"
        else if (length(grep("^file:", url))) {
            method <- "internal"
            url <- URLdecode(url)
        }
        else if (system("wget --help > /dev/null") == 0L) 
            method <- "wget"
        else if (system("curl --help > /dev/null") == 0L) 
            method <- "curl"
        else if (system("lynx -help > /dev/null") == 0L) 
            method <- "lynx"
        else stop("no download method found")
    }
    if (method == "internal") {
        status <- .External(C_download, url, destfile, quiet, 
            mode, cacheOK)
        if (!quiet) 
            flush.console()
    }

【讨论】:

  • 感谢您指向源代码。但是,由于 R 正在调用外部函数 C_download(而不是 .Internal().Primitive()),我找不到它的源代码。所以,在跨平台调用 .External(C_download) 意味着什么还不是很清楚。
猜你喜欢
  • 1970-01-01
  • 2012-06-19
  • 1970-01-01
  • 1970-01-01
  • 2019-10-06
  • 2015-03-20
  • 2014-06-06
  • 2020-08-18
  • 1970-01-01
相关资源
最近更新 更多