2013-03-17 11 views
7

W RCurl zdefiniowano funkcję i klasę CFILE do pracy z uchwytami plików na poziomie C. Z podręcznika:Tworzenie uchwytu pliku na poziomie RCurl do zapisywania pobranych plików

Zamysłem jest, aby móc je przekazać do libcurl jako opcje, aby można było odczytać lub zapisać z pliku lub do pliku. Możemy to zrobić również z połączeniami R i określamy funkcje oddzwaniania, które manipulują tymi połączeniami. Ale użycie uchwytu FILE o poziomie C może być znacznie szybsze w przypadku dużych plików.

Brak przykłady związane pobrań więc próbowałem:

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 = [email protected]) 

Próbowałem też poprzez zastąpienie opcję file z writedata = [email protected]. Plik został pobrany, ale jest uszkodzony. Pisanie niestandardowego wywołania zwrotnego dla argumentu write działa tylko dla danych niebinarnych.

Masz pomysł na pobranie pliku binarnego bezpośrednio na dysk (bez wczytywania go do pamięci) w RCurl?

Odpowiedz

6

myślę chcesz użyć writedata i pamiętaj, aby zamknąć plik

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

Dla bardziej skomplikowanej formie pisemnej, nie jestem pewien, czy jest to najlepszy sposób, ale Linux mówi mi, ze

man curl_easy_setopt 

że istnieje opcja CURL_WRITEFUNCTION curl który jest wskaźnikiem do funkcji C z prototypem

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

aw R na końcu instrukcji curlPerform jest przykład wywołania funkcji C jako opcji "zapisu". Więc stworzyłem plik 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; 
} 

skompilowany

R CMD SHLIB curl_writer.c 

który na Linuksie produkuje curl_writer.so pliku, a następnie w R

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

i dostać się na stderr

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

Te dwa pomysły mogą być zintegrowane, czyli pisanie do dowolnego pliku, używając dowolnej funkcji, poprzez modyfikowanie funkcji C, aby użyć pliku * mijamy się, jak

#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; 
} 

a następnie z powrotem w R po kompilacji

dyn.load("curl_writer.so") 
writer <- getNativeSymbolInfo("writer", PACKAGE="curl_writer")$address 
f <- CFILE(filename <- tempfile(), "wb") 
curlPerform(URL=url, [email protected], writefunction=writer) 
close(f) 

getURL może być również używany tutaj, pod warunkiem, [email protected], write=writer; Myślę, że problem z pierwotnym pytaniem jest taki, że R_curl_write_binary_data jest naprawdę wewnętrzną funkcją, zapisującą do bufora zarządzanego przez RCurl, a nie uchwyt pliku taki jak ten stworzony przez CFILE.Podobnie określenie writedata bez write (która wydaje się z kodu źródłowego, aby getURL był aliasem dla funkcji zapisu) wysyła wskaźnik do pliku do funkcji, która oczekuje wskaźnika na coś innego; dla getURL należy podać zarówno writedata, jak i write.

+0

Dzięki. Jak napisałem, próbowałem 'getURL (url = url, writedata = f @ ref)', który nie działa. Tak więc wydaje się, że tylko podzbiór parametrów w 'listCurlOptions()' może być faktycznie przekazany do 'getURL'. Niektóre są akceptowane tylko przez 'curlPerform'. Nie sądzę, że jest to wspomniane w instrukcji. – antonio

+0

@antonio z widoku 'getURL' i kodu źródłowego RCurl, domyślny argument' write' nie jest odpowiedni dla niestandardowego pliku, a R_curl_write_binary_data działa na wewnętrznej strukturze danych, a nie na uchwycie pliku; podanie argumentów 'write' i' writedata' jest wystarczające, myślę, że używam getURL. –

+0

Tak jak powiedziałeś, trzeba spojrzeć na kod źródłowy. W instrukcji mogą być pomocne dodatkowe wskazówki. – antonio

1

Pracuję również nad tym problemem i nie mam jeszcze odpowiedzi.

Jednak znalazłem to:

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

Pracujesz na badania pod Windows? Jestem.

Ta dokumentacja funkcji writedata wskazuje, że w systemie Windows użytkownik musi używać funkcji write wraz z writedata.

Czytanie tutaj: http://www.omegahat.org/RCurl/RCurlJSS.pdf Znalazłem, że RCurl oczekuje, że funkcja zapisu będzie funkcją R, więc możemy to zaimplementować sami w oknach. Będzie wolniej niż przy użyciu funkcji C do zapisu danych, jednak założę się, że prędkość łącza sieciowego będzie wąskim gardłem.

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 

(Jest to po utworzeniu pliku 1GB na serwerze, aby przetestować szybkość transferu)

jeszcze nie znalazłem odpowiedzi, która nie zadławić NUL bajtów danych. Wydaje się, że gdzieś w trzewiach pakietu RCurl, gdy przekazuje dane do R, aby wykonać funkcję zapisu, którą podasz, próbuje przekształcić dane w ciąg znaków. Nie wolno tego robić, jeśli używasz funkcji C. W szczególności za pomocą zalecanego wywołania zwrotnego R_curl_write_binary_data wraz z CFILE zabija rsession.exe na Win32 za każdym razem dla mnie.

Powiązane problemy