首页 > 解决方案 > 中止 SFTP 上传会在部分上传的文件末尾留下损坏的数据,从而无法继续上传

问题描述

我的应用程序使用 libcurl 将文件上传到 SFTP 服务器,有时我需要在当前上传完成之前关闭应用程序。我想中止上传,稍后再继续。但是,当上载中止时,服务器上部分上载文件的末尾会出现损坏的数据。数量不一,最多约 10 个字节。这使得无法通过附加数据来恢复上传。

请参阅下面的一些示例代码。按 Ctrl-C 中止上传,然后手动下载部分文件并与原始文件进行比较。部分文件最后通常会包含不正确的数据。

#include <stdio.h>
#include <signal.h>
#include "curl.h"

// Handle Ctrl-C to abort.
static bool s_bAborted = false;
static void __cdecl ctrlCHandler(int)
{
    printf("Aborting...\n");
    s_bAborted = true;
}

// File reading function.
static size_t readFunction(char * ptr, size_t size, size_t nmemb, void * stream)
{
    FILE * pFile = (FILE *)stream;
    if (ferror(pFile))
    {
        return CURL_READFUNC_ABORT;
    }
    return fread(ptr, size, nmemb, pFile);
}

// Progress function so transfers can be aborted.
static int progressFunction(void * clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
{
    if (s_bAborted)
    {
        return 1;
    }
    return CURL_PROGRESSFUNC_CONTINUE;
}

int main()
{
    // Add your local file and server URL.
    // Note: This problem was observed using an SFTP server.
    const char * szLocalFile = "Test.bin";
    const char * szRemoteUrl = "sftp://user:password@host/Test.bin";

    curl_global_init(CURL_GLOBAL_ALL);
    CURL * curl = curl_easy_init();
    FILE * pFile = fopen(szLocalFile, "rb");

    curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
    curl_easy_setopt(curl, CURLOPT_URL, szRemoteUrl);
    curl_easy_setopt(curl, CURLOPT_READFUNCTION, readFunction);
    curl_easy_setopt(curl, CURLOPT_READDATA, pFile);
    curl_easy_setopt(curl, CURLOPT_APPEND, 0L);
    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
    curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progressFunction);

    printf("Uploading... Press Ctrl-C to abort\n");
    signal(SIGINT, ctrlCHandler);

    CURLcode eResult = curl_easy_perform(curl);
    fclose(pFile);

    if (eResult == CURLE_OK)
    {
        printf("Uploaded OK\n");
    }
    else if (eResult == CURLE_ABORTED_BY_CALLBACK)
    {
        printf("Upload aborted\n");
    }
    else
    {
        printf("Error: Upload failed: %d\n", eResult);
    }

    curl_easy_cleanup(curl);
    curl_global_cleanup();

    // After aborting the upload, use something else to download the partial file.
    // The last few bytes of the partial file are usually incorrect,
    // i.e. different to the original file in that position.
    // This makes resuming the upload impossible, since it will append after incorrect bytes.
}

请问有人可以帮忙吗?谢谢你。

标签: c++clibcurl

解决方案


按照上面 rustyx 的建议,我将其修改为使用 read 函数中止,并删除了进度回调。这似乎可以解决问题(经过大约 15 次测试后)。

注意:此方法意味着部分文件始终是 64 kB 的倍数。从进度回调中止时,它是任何随机大小。也许这与它有关。

请参阅下面的更新代码:

#include <stdio.h>
#include <signal.h>
#include "curl.h"

// Handle Ctrl-C to abort.
static bool s_bAborted = false;
static void __cdecl ctrlCHandler(int)
{
    s_bAborted = true;
}

// File reading function.
static size_t readFunction(char * ptr, size_t size, size_t nmemb, void * stream)
{
    FILE * pFile = (FILE *)stream;
    if (s_bAborted || ferror(pFile))
    {
        return CURL_READFUNC_ABORT;
    }
    return fread(ptr, size, nmemb, pFile);
}

int main()
{
    // Add your local file and server URL.
    // Note: This problem was observed using an SFTP server.
    const char * szLocalFile = "Test.bin";
    const char * szRemoteUrl = "sftp://user:password@host/Test.bin";

    curl_global_init(CURL_GLOBAL_ALL);
    CURL * curl = curl_easy_init();
    FILE * pFile = fopen(szLocalFile, "rb");

    curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
    curl_easy_setopt(curl, CURLOPT_URL, szRemoteUrl);
    curl_easy_setopt(curl, CURLOPT_READFUNCTION, readFunction);
    curl_easy_setopt(curl, CURLOPT_READDATA, pFile);
    curl_easy_setopt(curl, CURLOPT_APPEND, 0L);

    printf("Uploading... Press Ctrl-C to abort\n");
    signal(SIGINT, ctrlCHandler);

    CURLcode eResult = curl_easy_perform(curl);
    fclose(pFile);

    if (eResult == CURLE_OK)
    {
        printf("Uploaded OK\n");
    }
    else if (eResult == CURLE_ABORTED_BY_CALLBACK)
    {
        printf("Upload aborted\n");
    }
    else
    {
        printf("Error: Upload failed: %d\n", eResult);
    }

    curl_easy_cleanup(curl);
    curl_global_cleanup();
}

推荐阅读