cURL / Mailing Lists / curl-library / Single Mail

curl-library

How to configure easy handle for FTP upload resume?

From: Marcin Adamski <mass85_at_tlen.pl>
Date: Wed, 09 Nov 2011 17:15:02 +0100

I have a problem with transfer resume after connection had been lost and only a part of file has been uploaded to FTP server.

1. Size of uploaded file on server after resume is less than size of original file when I set only this:
  Set_Easy_Opt(handle, CURLOPT_APPEND, 1l);
2. Size of uploaded file on server after resume is greater than size of original file when I set this among other options:
  Set_Easy_Opt(handle, CURLOPT_RESUME_FROM, -1l);
  or
  Set_Easy_Opt(handle, CURLOPT_RESUME_FROM_LARGE, -1l);
It seems that in this case we have a sume of partially uploaded file and fully uploaded file.

When I try to upload files using this command:
curl -T /tmp/file4upload ftp://10.4.0.1/upload/foo10 -v -u user:pass -C -1
it worked.

I have tried various combinations of these settings and neither of them works. I also tried to check how it is done in CURL sources, but it didn't give me any results.

How should I configure my handle to make it work?

Here is link to code:
http://pastebin.com/YMmKxpbh

and the code itself:

template<typename OptionType>
void Set_Easy_Opt(CURL* easyHandle, CURLoption option, OptionType value) throw(std::exception)
{
    CURLcode result = curl_easy_setopt(easyHandle, option, value);
    if (result != CURLE_OK)
    {
        std::ostringstream str;
        str << "curl_easy_setopt FAILED for option: " << option << ", value: " << value
                        << " returned: " << (int) result << " " << curl_easy_strerror(result);
        throw std::runtime_error(str.str());
    }
}

tm_error_t TransferManager::SetupTransfer(transfer_data& transfer, CURL* handle)
{
        const long MIN_OPERATION_TIME = 20;
        const long FTP_RESPONSE_TIMEOUT = 90;
        const long CONNECT_TIMEOUT = 60;
    
        long timeout = difftime(transfer.task.expireTime, std::time(0));
        if (timeout < MIN_OPERATION_TIME)
        {
                LOG_INFO("There is not enough time for the operation: " << timeout
                                        << " s, minimum is: " << MIN_OPERATION_TIME << " s");
                Fclose(transfer.fileHandle);
        
                return TM_OP_TOUT;
        }
        long connectTimeout = (timeout > CONNECT_TIMEOUT) ? CONNECT_TIMEOUT : timeout/2;
        long ftpResponseTimeout = (FTP_RESPONSE_TIMEOUT < timeout) ? FTP_RESPONSE_TIMEOUT : (timeout - 1);
        
        // for now it's only for upload
        if (not transfer.fileHandle) // it can be initial try
        {
                transfer.fileHandle = fopen(transfer.task.GetLocalPath().c_str(), "r");
                if (not transfer.fileHandle)
                {
                        LOG_ERROR("CURL: Could not open file handle to: " << transfer.task.GetLocalPath());
                        return TM_FILE_ERR;
                }
        }
        
        try {
                curl_easy_reset(handle);

                transfer.errorBuffer[0] = '\0'; // we clean buffer before every retry of transfer
                Set_Easy_Opt(handle, CURLOPT_ERRORBUFFER, transfer.errorBuffer);

                Set_Easy_Opt(handle, CURLOPT_URL, transfer.task.urlToFile.c_str());
                
                Set_Easy_Opt(handle, CURLOPT_UPLOAD, 1l);
                Set_Easy_Opt(handle, CURLOPT_APPEND, 1l);
                //Set_Easy_Opt(handle, CURLOPT_RESUME_FROM, -1l); // continue upload
                //Set_Easy_Opt(handle, CURLOPT_RESUME_FROM_LARGE, -1l); // continue upload
                Set_Easy_Opt(handle, CURLOPT_READDATA, transfer.fileHandle);
                

                std::string userPass = mFtpUser + ":" + mFtpPass;
                Set_Easy_Opt(handle, CURLOPT_USERPWD, userPass.c_str());

                Set_Easy_Opt(handle, CURLOPT_NOSIGNAL, 1l);
                
                LOG_DEBUG("Setting timeout for task with " << transfer.task.urlToFile
                                        << ", timeout = " << timeout << ", connect timeout = " << connectTimeout
                    << ", ftpResponseTimeout = " << ftpResponseTimeout);
                Set_Easy_Opt(handle, CURLOPT_TIMEOUT, timeout);
                Set_Easy_Opt(handle, CURLOPT_CONNECTTIMEOUT, connectTimeout);
                Set_Easy_Opt(handle, CURLOPT_FTP_RESPONSE_TIMEOUT, ftpResponseTimeout);

#ifdef CURL_DEBUG_CALLBACK
                Set_Easy_Opt(handle, CURLOPT_VERBOSE, 1l);
                Set_Easy_Opt(handle, CURLOPT_DEBUGFUNCTION, CurlDebugCallback);
#endif
                Set_Easy_Opt(handle, CURLOPT_LOW_SPEED_TIME, 60l);
                Set_Easy_Opt(handle, CURLOPT_LOW_SPEED_LIMIT, 1l);

                Set_Easy_Opt(handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER);
        } catch (std::runtime_error const& e) {
                LOG_ERROR("CURL: easy handle could not be configured for server task with url: \""
                    << transfer.task.urlToFile << "\", local path: \"" << transfer.task.GetLocalPath()
                    << "\", exception: " << e.what());
                if (transfer.errorBuffer[0])
                        LOG_ERROR("CURL: ConfigureCurlHandle: curl error buffer: " << transfer.errorBuffer);
                Fclose(transfer.fileHandle);
                return TM_SOFTWARE_ERR;
        }
        
        CURLMcode curlResult = curl_multi_add_handle(mMultiHandle, handle);
        if (curlResult != CURLM_OK)
        {
                LOG_ERROR("CURL: easy handle could not be added to multi!!!! error code: "
                                        << (int) curlResult << " " << curl_multi_strerror(curlResult));
                Fclose(transfer.fileHandle);
                
                return TM_SOFTWARE_ERR;
        }
        
        return TM_NO_ERR;
}

Regards
Marcin Adamski

-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette: http://curl.haxx.se/mail/etiquette.html
Received on 2011-11-09