8

I am trying to download file using FTP, and in between if connection terminates then it should resume from where it was stop. My problem is that using following code snippet I able to continue download if I close connection and then again connect it, but if I am do so at server site then I am not able to resume download, and program goes into infinites state.

#include <stdio.h>

#include <curl/curl.h>

/*
 * This is an example showing how to get a single file from an FTP server.
 * It delays the actual destination file creation until the first write
 * callback so that it won't create an empty file in case the remote file
 * doesn't exist or something else fails.
 */

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


int main(void)
{
  CURL *curl;
  CURLcode res;
  struct FtpFile ftpfile={
    "dev.zip", /* name to store the file as if succesful */
    NULL
  };

  curl_global_init(CURL_GLOBAL_DEFAULT);

  curl = curl_easy_init();
  if(curl) {
    /*
     * You better replace the URL with one that works!
     */
    curl_easy_setopt(curl, CURLOPT_URL,
                     "ftp://root:password@192.168.10.1/dev.zip");
    /* Define our callback to get called when there's data to be written */
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite);
    /* Set a pointer to our struct to pass to the callback */
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ftpfile);

    /* Switch on full protocol/debug output */
    curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

    res = curl_easy_perform(curl);

    /* always cleanup */
    curl_easy_cleanup(curl);

    if(CURLE_OK != res) {
      /* we failed */
      fprintf(stderr, "curl told us %d\n", res);
    }
  }

  if(ftpfile.stream)
    fclose(ftpfile.stream); /* close the local file */

  curl_global_cleanup();

  return 0;
}

Could any one tell me that how can I resume download if connection is closed by remote site. Any help would be appreciated

Thanks,

Yuvi

Yuvi
  • 1,344
  • 4
  • 24
  • 46
  • Is there some thing like http's 'AddRange' in ftp? – Vikram.exe Feb 23 '12 at 10:39
  • yes it is there but my problem is that I am not able to catch error if connection is closed, to know tad more you can check it here http://curl.haxx.se/libcurl/c/ftpuploadresume.html – Yuvi Feb 23 '12 at 11:03

2 Answers2

2

Add a varaible to the ftpfile struct to aware your write function of the need to appeand and tell libcurl to resume the download from the end of the destination file by setting CURLOPT_RESUME_FROM to the number of bytes downloaded already:

 struct FtpFile 
 { 
   const char *pcInfFil; 
   FILE *pFd; 
   int iAppend; 
 };

In main, if you want to resume:

curl_easy_setopt(curl, CURLOPT_RESUME_FROM , numberOfBytesToSkip); 

If you the file doesn't already exist, or it is not a resumed download but a new download, be sure to set CURLOPT_RESUME_FROM back to 0.

In my_fwrite:

out->stream=fopen(out->filename, out->iAppend ? "ab":"wb"); 

P.S. if where you need to resume the file is larger than a long (2GB), look into CURLOPT_RESUME_FROM_LARGE and CURL_OFF_T_C()

In response to comment requesting additional information on how to know when a transfer failed:

After you call curl easy perform call:

CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ... );

Retrieve from the curl context:

CURLINFO_HEADER_SIZE 
CURLINFO_CONTENT_LENGTH_DOWNLOAD

Add them together and make sure they equal

CURLINFO_SIZE_DOWNLOAD

If not try to reperform the context.

And be sure to use the latest version of curl, it should timeout after 60 seconds of not hearing from the FTP server it is downloading from.

Motomotes
  • 4,111
  • 1
  • 25
  • 24
  • thanks for answer, but how do i get to know that connection is closed by remote site, that sample program doesn't go if condition, it remains in infinite state... – Yuvi Feb 24 '12 at 05:35
0

You may use the CURLOPT_CONNECTTIMEOUT and CURLOPT_TIMEOUT parameters to specify a connect timeout and maximum execution time on a per-handle basis.

The other way (that only works if you're using the easy interface, not the multi one) is to use a socket option callback, that you can set using CURLOPT_SOCKOPTFUNCTION. In it, you must call setsockopt() for the SO_RCVTIMEO parameter to the maximum amount of time a connection can be idle before it should be dropped. i.e. if no bytes have been received in the last 5 seconds, then drop the connection.

Radu Dan
  • 73
  • 1
  • 5