c++ - 中止 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.
}
请问有人可以帮忙吗?谢谢你。
解决方案
按照上面 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();
}
推荐阅读
- c# - 如果单击行并将其列从 true 更改为 false,我如何在 DataGrid 中检测到,反之亦然?
- javascript - 如何在 javascript 中使用另一个数组中的属性过滤一个数组中的对象?
- angular - 角度条件
- javascript - 在 Javascript 中获取 Bootstrap 的当前主题颜色
- terraform - 如何以编程方式获取 Terraform 模块名称?
- java - BeanNotOfRequiredTypeException:名为 X 的 Bean 应该属于 X 类型,但实际上属于“com.sun.proxy.$Proxy”类型
- rspec - 为 RSpec rails 5 创建未定义的方法
- ios - 将 View 动画到 collectionView 中的选定单元格或 scrollView 中的 tableView
- amazon-s3 - 为什么 october cms 不通过使用 amazon-s3 上传图像将附件保存在 db 上?
- i2c - I2C 通信有什么问题?