Создайте дескриптор файла уровня C в RCurl для записи загруженных файлов.

В RCurl определена функция и класс CFILE для работы с дескрипторами файлов уровня C. Из руководства:

Цель состоит в том, чтобы иметь возможность передавать их в libcurl в качестве параметров, чтобы он мог читать или записывать из файла или в файл. Мы также можем сделать это с подключениями R и указать функции обратного вызова, которые управляют этими подключениями. Но использование дескриптора FILE уровня 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?


person antonio    schedule 17.03.2013    source источник


Ответы (2)


Я думаю, вы хотите использовать 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);

а в R в конце ?curlPerform есть пример вызова функции 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

который в линуксе создает файл 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 кажется псевдонимом функции записи) отправляет указатель на файл функции, ожидающей указатель на что-то еще; для getURL необходимо предоставить как данные записи, так и запись.

person Martin Morgan    schedule 17.03.2013
comment
Спасибо. Как я уже писал, я пробовал getURL(url = url, writedata = f@ref), который не работает. Таким образом, кажется, что только подмножество параметров в listCurlOptions() может быть фактически передано в getURL. Некоторые принимаются только curlPerform. Я не думаю, что это упоминается в инструкции. - person antonio; 18.03.2013
comment
@antonio из просмотра getURL и исходного кода RCurl, аргумент по умолчанию write не подходит для пользовательского файла, а R_curl_write_binary_data работает с внутренней структурой данных, а не с дескриптором файла; достаточно предоставить аргументы write и writedata, я думаю использовать getURL. - person Martin Morgan; 18.03.2013
comment
Как вы сказали, нужно смотреть на исходный код. Еще несколько советов в руководстве могут оказаться полезными. - person antonio; 19.03.2013

Я тоже работаю над этой проблемой, но ответа пока нет.

Тем не менее, я нашел это:

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

Вы работаете над R под Windows? Я.

В этой документации для функции записи данных указано, что в Windows вы должны использовать функцию записи вместе с записью данных.

Читать здесь: http://www.omegahat.org/RCurl/RCurlJSS.pdf I обнаружил, что RCurl ожидает, что функция записи будет функцией 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

(Это после создания файла размером 1 ГБ на сервере для проверки скорости передачи)

Я еще не нашел ответа, который не захлебывается байтами NUL в данных. Кажется, что где-то в недрах пакета RCurl, когда он передает данные в R для выполнения предоставленной вами функции записи, он пытается преобразовать данные в строку символов. Этого не должно быть, если вы используете функцию C. Примечательно, что использование рекомендуемого обратного вызова R_curl_write_binary_data вместе с CFILE каждый раз убивает rsession.exe на win32 для меня.

person Keith Twombley    schedule 30.05.2013