在RCurl中,定义了一个函数和一个类CFILE
来处理C级文件句柄。从手册:
目的是能够将这些作为选项传递给libcurl,以便它可以从文件中读取或写入文件。我们也可以使用R连接执行此操作并指定操作这些连接的回调函数。但是对于大型文件来说,使用C级文件句柄的速度可能会快得多。
没有与下载相关的示例,所以我尝试了:
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中将二进制文件直接下载到磁盘(不加载到内存中)?
答案 0 :(得分:6)
我认为你想使用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
创建的文件句柄。同样地,指定writedata
而不是write
(从源代码看起来getURL是writefunction的别名)将指向文件的指针发送到期望指向别的东西的函数;对于getURL,需要提供writedata和write。
答案 1 :(得分:1)
我正在解决这个问题,但还没有答案。
然而,我确实找到了这个:
http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTWRITEDATA
你在Windows下工作吗?我是。
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文件以测试传输速度之后)
我还没有找到一个不会阻塞数据中NUL字节的答案。当它将数据传递到R以执行您提供的写入功能时,它似乎在RCurl包的内部某处,它尝试将数据转换为字符串。如果使用C函数,则不能这样做。值得注意的是,每次使用推荐的R_curl_write_binary_data回调和CFILE都会在win32上杀死rsession.exe。