curl-users
Re: How to resume file download through FTP ?
Date: Thu, 23 Feb 2012 14:41:15 +0100
On Thu, Feb 23, 2012 at 03:12:40PM +0530, Yuvi yuvi wrote:
> I have searched a lot but don't find any sample code for resume download
> through File Transfer Code. I know that curl support this feature and I can
> able to do this using command prompt. But how to do it programmatically. Even I
> tired to write one but no luck, this code working when connection is closed at
> my lapi but if connection is closed by remote site it is not working :
You can use the --libcurl option in the command-line tool to see what
options it sets. Resuming also requires seek logic in the client,
though, which --libcurl won't give you.
> #include <stdlib.h>
> #include <stdio.h>
>
> #include <curl/curl.h>
>
>
> struct FtpFile {
>
> const char *filename;
>
> FILE *stream;
> };
>
> static size_t my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream)
>
> {
> struct FtpFile *out=(struct FtpFile *)stream;
>
> if(out && !out->stream) {
>
> /* open file for writing */
> out->stream=fopen(out->filename, "wb");
>
> if(!out->stream)
> return -1; /* failure, can't open file to write */
>
> }
> return fwrite(buffer, size, nmemb, out->stream);
>
> }
>
>
>
> static size_t append_fwrite(void *buffer, size_t size, size_t nmemb, void *stream)
>
> {
> struct FtpFile *out=(struct FtpFile *)stream;
>
> if(out && !out->stream) {
>
> // open file for writing
> out->stream=fopen(out->filename, "a+");
The other fopen uses the 'b' modifier, so this ought to as well.
> if(!out->stream)
> return -1; //failure, can't open file to write
>
> }
> return fwrite(buffer, size, nmemb, out->stream);
>
> }
>
>
>
> /* parse headers for Content-Length */
> size_t getcontentlengthfunc(void *ptr, size_t size, size_t nmemb, void *stream)
>
> {
> int r;
> long len = 0;
>
>
> /* _snscanf() is Win32 specific */
> r = sscanf(ptr, "Content-Length: %ld\n", &len);
This will fail if the server sends this line with different case. I
believe recent versions of libcurl guarantee that curl_easy_getinfo with
CURLINFO_CONTENT_LENGTH_DOWNLOAD will return that value (when available
at all) by the time the first write callback is called. That would be a
more reliable way of determining the length.
>
> if (r) /* Microsoft: we don't read the specs */
>
> *((long *) stream) = len;
>
> return size * nmemb;
> }
>
>
> /* discard downloaded data */
> size_t discardfunc(void *ptr, size_t size, size_t nmemb, void *stream)
This isn't referenced anywhere. And throwing out all downloaded data is
usually pretty wasteful. Using NOBODY as you've done is much better.
> {
> return size * nmemb;
>
> }
>
>
>
>
> int download(CURL *curlhandle, const char * remotepath,long timeout, long tries)
>
> {
>
> long uploaded_len = 0;
>
> CURLcode r = CURLE_GOT_NOTHING;
> int c;
>
> struct FtpFile ftpfile={
> "dev.zip", //name to store the file as if succesful
>
> NULL
> };
>
>
>
>
> curl_easy_setopt(curlhandle, CURLOPT_URL, remotepath);
>
>
> if (timeout)
> curl_easy_setopt(curlhandle, CURLOPT_FTP_RESPONSE_TIMEOUT, timeout);
>
>
> curl_easy_setopt(curlhandle, CURLOPT_HEADERFUNCTION, getcontentlengthfunc);
>
> curl_easy_setopt(curlhandle, CURLOPT_HEADERDATA, &uploaded_len);
I don't see where this value is used.
>
> curl_easy_setopt(curlhandle, CURLOPT_WRITEFUNCTION, my_fwrite);
>
> curl_easy_setopt(curlhandle, CURLOPT_WRITEDATA, &ftpfile);
>
> curl_easy_setopt(curlhandle, CURLOPT_FTPPORT, "-"); /* disable passive mode */
>
> curl_easy_setopt(curlhandle, CURLOPT_FTP_CREATE_MISSING_DIRS, 1L);
>
>
> curl_easy_setopt(curlhandle, CURLOPT_VERBOSE, 1L);
>
>
> for (c = 0; (r != CURLE_OK) && (c < tries); c++) {
>
> /* are we resuming? */
> if (c) { /* yes */
This logic is too simplistic. The first attempt may have failed well
before the connection to the server was even established.
> /* determine the length of the file already written */
> fputs("retrying.............",stderr);
>
> curl_easy_setopt(curlhandle, CURLOPT_NOBODY, 1L);
>
> curl_easy_setopt(curlhandle, CURLOPT_HEADER, 1L);
>
>
> r = curl_easy_perform(curlhandle);
Using curl_easy_getinfo eliminates the need for this separate transfer.
> if (r != CURLE_OK)
>
> continue;
>
> curl_easy_setopt(curlhandle, CURLOPT_NOBODY, 0L);
>
> curl_easy_setopt(curlhandle, CURLOPT_HEADER, 0L);
>
>
>
>
> curl_easy_setopt(curlhandle, CURLOPT_APPEND, append_fwrite);
CURLOPT_APPEND takes a long, not a function pointer. I think you want
CURLOPT_WRITEFUNCTION here.
What you're missing is an ftell or fstat call and the CURLOPT_RANGE or
CURLOPT_RESUME_FROM option to have the server start sending data from
the last location downloaded.
>
> curl_easy_setopt(curlhandle, CURLOPT_VERBOSE, 1L);
>
> }
> else { /* no */
>
> curl_easy_setopt(curlhandle, CURLOPT_APPEND, 0L);
>
> curl_easy_setopt(curlhandle, CURLOPT_VERBOSE, 1L);
>
> }
>
> r = curl_easy_perform(curlhandle);
>
> }
>
>
>
> if (r == CURLE_OK)
>
> return 1;
> else {
>
> fprintf(stderr, "%s\n", curl_easy_strerror(r));
>
> return 0;
> }
> }
>
>
> int main(int c, char **argv)
>
> {
> CURL *curlhandle = NULL;
>
>
> curl_global_init(CURL_GLOBAL_ALL);
> curlhandle = curl_easy_init();
>
>
> download(curlhandle, "ftp://root:password@192.168.10.1/dev.zip", 0, 100);
>
>
> curl_easy_cleanup(curlhandle);
> curl_global_cleanup();
>
>
> return 0;
> }
>>> Dan
-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-users
FAQ: http://curl.haxx.se/docs/faq.html
Etiquette: http://curl.haxx.se/mail/etiquette.html
Received on 2012-02-23