8

在 RCurl 中,定义了一个函数和一个类CFILE来处理 C 级文件句柄。从手册:

目的是能够将这些作为选项传递给 libcurl,以便它可以读取或写入文件。我们也可以使用 R 连接来执行此操作,并指定操作这些连接的回调函数。但是对于大文件,使用 C 级 FILE 句柄可能会明显更快。

没有与下载相关的示例,所以我尝试了:

library(RCurl)
u = "http://cran.r-project.org/web/packages/RCurl/RCurl.pdf"
f = CFILE("RCurl.pdf", mode="wb")
ret= getURL(u,  write = getNativeSymbolInfo("R_curl_write_binary_data")$address,
                file  = f@ref)

我还尝试将file选项替换为writedata = f@ref. 文件已下载,但已损坏。为参数编写自定义回调write仅适用于非二进制数据。

在 RCurl 中将二进制文件直接下载到磁盘(而不将其加载到内存中)的任何想法?

4

2 回答 2

7

我想你要使用writedata并记得关闭文件

library(RCurl)
filename <- tempfile()
f <- CFILE(filename, "wb")
url <- "http://cran.fhcrc.org/Rlogo.jpg"
curlPerform(url = url, writedata = f@ref)
close(f)

对于更详细的写作,我不确定这是否是最好的方式,但 Linux 告诉我,从

man curl_easy_setopt

有一个 curl 选项 CURL_WRITEFUNCTION 它是一个指向带有原型的 C 函数的指针

size_t function(void *ptr, size_t  size, size_t nmemb, void *stream);

在 ?curlPerform 末尾的 R 中有一个调用 C 函数作为“writefunction”选项的示例。所以我创建了一个文件 curl_writer.c

#include <stdio.h>

size_t
writer(void *buffer, size_t size, size_t nmemb, void *stream)
{
    fprintf(stderr, "<writer> size = %d, nmemb = %d\n",
            (int) size, (int) nmemb);
    return size * nmemb;
}

编译它

R CMD SHLIB curl_writer.c

在 Linux 上生成一个文件 curl_writer.so,然后在 R

dyn.load("curl_writer.so")
writer <- getNativeSymbolInfo("writer", PACKAGE="curl_writer")$address
curlPerform(URL=url, writefunction=writer)

并上 stderr

<writer> size = 1, nmemb = 2653
<writer> size = 1, nmemb = 520
OK 

这两个想法可以整合,即使用任意函数写入任意文件,通过修改C函数使用我们传入的FILE *,如

#include <stdio.h>

size_t
writer(void *buffer, size_t size, size_t nmemb, void *stream)
{
    FILE *fout = (FILE *) stream;
    fprintf(fout, "<writer> size = %d, nmemb = %d\n",
            (int) size, (int) nmemb);
    fflush(fout);
    return size * nmemb;
}

然后在编译后返回 R

dyn.load("curl_writer.so")
writer <- getNativeSymbolInfo("writer", PACKAGE="curl_writer")$address
f <- CFILE(filename <- tempfile(), "wb")
curlPerform(URL=url, writedata=f@ref, writefunction=writer)
close(f)

getURL也可以在这里使用,前提是writedata=f@ref, write=writer;我认为原始问题中的问题R_curl_write_binary_data实际上是一个内部函数,写入由 RCurl 管理的缓冲区,而不是像CFILE. 同样,指定writedatawithout write(从源代码看来 getURL 是 writefunction 的别名)将指向文件的指针发送到期望指向其他指针的函数;对于 getURL,需要提供 writedata 和 write。

于 2013-03-17T16:43:13.330 回答
1

我也在解决这个问题,但还没有答案。

但是,我确实发现了这个:

http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTWRITEDATA

你在 Windows 下使用 R 吗?我是。

writedata 函数的此文档表明,在 Windows 上,您必须将 writefunction 与 writedata 一起使用。

在这里阅读:http ://www.omegahat.org/RCurl/RCurlJSS.pdf 我发现 RCurl 期望 writefunction 是一个 R 函数,所以我们可以自己在 Windows 上实现它。它会比使用 C 函数写入数据要慢,但我敢打赌,网络链接的速度将是瓶颈。

getURI(url="sftp://hostname/home/me/onegeebee", curl=con, write=function(x) writeChar(x, f, eos=NULL))
Error in curlPerform(curl = curl, .opts = opts, .encoding = .encoding) : embedded nul in string: ' <`á\017_\021

(这是在服务器上创建一个 1GB 的文件以测试传输速度之后)

I haven't yet found an answer that doesn't choke on NUL bytes in the data. It seems that somewhere in the bowels of the RCurl package when it's passing data up into R to execute the writefunction you supply, it tries to convert the data into a character string. It must not do that if you use a C function. Notably using the recommended R_curl_write_binary_data callback along with CFILE kills rsession.exe on win32 every time for me.

于 2013-05-30T20:07:53.543 回答