ToB企服应用市场:ToB评测及商务社交产业平台

标题: C++文件系统操作6 - 跨平台实现文件和文件夹的拷贝 [打印本页]

作者: 知者何南    时间: 2024-7-26 21:55
标题: C++文件系统操作6 - 跨平台实现文件和文件夹的拷贝
1. 关键词

C++ 文件系统操作 拷贝文件 拷贝文件夹 跨平台
2. fileutil.h
  1. #pragma once
  2. #include <string>
  3. #include <cstdio>
  4. #include <cstdint>
  5. #include "filetype.h"
  6. #include "filepath.h"
  7. namespace cutl
  8. {
  9.     /**
  10.      * @brief The file guard class to manage the FILE pointer automatically.
  11.      * file_guard object can close the FILE pointer automatically when his scope is exit.
  12.      */
  13.     class file_guard
  14.     {
  15.     public:
  16.         /**
  17.          * @brief Construct a new file guard object
  18.          *
  19.          * @param file the pointer of the FILE object
  20.          */
  21.         explicit file_guard(FILE *file);
  22.         /**
  23.          * @brief Destroy the file guard object
  24.          *
  25.          */
  26.         ~file_guard();
  27.         /**
  28.          * @brief Get the FILE pointer.
  29.          *
  30.          * @return FILE*
  31.          */
  32.         FILE *getfd() const;
  33.     private:
  34.         FILE *file_;
  35.     };
  36.     /**
  37.      * @brief Copy a file or symbolic link(shortcut on windows).
  38.      *
  39.      * @param srcpath the filepath of the source file or symbolic link to be copied
  40.      * @param dstpath the filepath of the destination file or symbolic link to be copied to
  41.      * @param attributes whether to copy the file attributes, default is false.
  42.      * If true, means copy the file attributes, like the 'cp -p' command.
  43.      * @return true if the file or symbolic link is copied successfully, false otherwise.
  44.      * @note If the destination file or directory already exists, it will be overwritten.
  45.      * @return false
  46.      */
  47.     bool copyfile(const filepath &srcpath, const filepath &dstpath, bool attributes = false);
  48.     // copy directory recursively
  49.     /**
  50.      * @brief Copy a directory recursively.
  51.      *
  52.      * @param srcdir the filepath of the source directory to be copied
  53.      * @param dstdir the filepath of the destination directory to be copied to
  54.      * @return true if the directory is copied successfully, false otherwise.
  55.      * @note If the destination directory already exists, it will be overwritten.
  56.      */
  57.     bool copydir(const filepath &srcdir, const filepath &dstdir);
  58. } // namespace cutl
复制代码
3. fileutil.cpp
  1. #include <cstdio>
  2. #include <map>
  3. #include <iostream>
  4. #include <cstring>
  5. #include <sys/stat.h>
  6. #include "fileutil.h"
  7. #include "inner/logger.h"
  8. #include "inner/filesystem.h"
  9. #include "strutil.h"
  10. namespace cutl
  11. {
  12.     file_guard::file_guard(FILE *file)
  13.         : file_(file)
  14.     {
  15.     }
  16.     file_guard::~file_guard()
  17.     {
  18.         if (file_)
  19.         {
  20.             // CUTL_DEBUG("close file");
  21.             int ret = fclose(file_);
  22.             if (ret != 0)
  23.             {
  24.                 CUTL_ERROR("fail to close file, ret" + std::to_string(ret));
  25.             }
  26.             file_ = nullptr;
  27.         }
  28.         // ROBOLOG_DCHECK(file_ == nullptr);
  29.     }
  30.     FILE *file_guard::getfd() const
  31.     {
  32.         return file_;
  33.     }
  34.     bool copyfile(const filepath &srcpath, const filepath &dstpath, bool attributes)
  35.     {
  36.         // CUTL_INFO("file type: " + std::to_string(srcpath.type()) + ", " + filetype_flag(srcpath.type()) + ", " + srcpath.str() + ", dstpath:" + dstpath.str());
  37.         // copy file content
  38.         if (srcpath.isfile())
  39.         {
  40.             if (dstpath.exists())
  41.             {
  42.                 // remove if exists
  43.                 removefile(dstpath);
  44.             }
  45.             file_guard frd(fopen(srcpath.str().c_str(), "rb"));
  46.             if (frd.getfd() == nullptr)
  47.             {
  48.                 CUTL_ERROR("open file failed, " + srcpath.str());
  49.                 return false;
  50.             }
  51.             file_guard fwt(fopen(dstpath.str().c_str(), "wb"));
  52.             if (fwt.getfd() == nullptr)
  53.             {
  54.                 CUTL_ERROR("open file failed, " + dstpath.str());
  55.                 return false;
  56.             }
  57.             static constexpr size_t buf_size = 8 * 1024;
  58.             uint8_t buffer[buf_size] = {0};
  59.             size_t read_len = 0;
  60.             size_t write_len = 0;
  61.             while ((read_len = fread(buffer, 1, buf_size, frd.getfd())) > 0)
  62.             {
  63.                 write_len = fwrite(buffer, 1, read_len, fwt.getfd());
  64.                 if (write_len != read_len)
  65.                 {
  66.                     CUTL_ERROR("write file failed, only write " + std::to_string(write_len) + ", read_len:" + std::to_string(read_len));
  67.                     return false;
  68.                 }
  69.             }
  70.             // flush file to disk
  71.             int ret = fflush(fwt.getfd());
  72.             if (0 != ret)
  73.             {
  74.                 CUTL_ERROR("fail to flush file:" + dstpath.str());
  75.                 return false;
  76.             }
  77.             if (!file_sync(fwt.getfd()))
  78.             {
  79.                 CUTL_ERROR("file_sync failed for " + dstpath.str());
  80.                 return false;
  81.             }
  82.         }
  83.         else if (srcpath.issymlink())
  84.         {
  85.             if (dstpath.exists())
  86.             {
  87.                 // remove if exists
  88.                 file_removelink(dstpath.str());
  89.             }
  90.             auto link_path = file_readlink(srcpath.str());
  91.             if (link_path.empty())
  92.             {
  93.                 CUTL_ERROR("readlink failed for " + srcpath.str());
  94.                 return false;
  95.             }
  96.             if (!file_createlink(link_path, dstpath.str()))
  97.             {
  98.                 CUTL_ERROR("createlink failed for " + dstpath.str());
  99.                 return false;
  100.             }
  101.         }
  102.         else
  103.         {
  104.             CUTL_ERROR("not a file or symlink, cannot copy: [" + filetype_flag(srcpath.type()) + "]" + srcpath.str());
  105.             return false;
  106.         }
  107.         // copy file attributes
  108.         if (attributes && srcpath.isfile())
  109.         {
  110.             return copy_attributes(srcpath.str(), dstpath.str());
  111.         }
  112.         return true;
  113.     }
  114.     // https://www.cnblogs.com/harrypotterjackson/p/12113382.html
  115.     bool copydir(const filepath &srcdir, const filepath &dstdir)
  116.     {
  117.         if (!srcdir.isdir())
  118.         {
  119.             CUTL_ERROR("srcdir is not a directory: " + srcdir.str());
  120.             return false;
  121.         }
  122.         if (!dstdir.exists() && !createdir(dstdir, true))
  123.         {
  124.             CUTL_ERROR("createdir failed for " + dstdir.str());
  125.             return false;
  126.         }
  127.         auto filelist = list_files(srcdir, filetype::all, true);
  128.         for (size_t i = 0; i < filelist.size(); i++)
  129.         {
  130.             auto file = filelist[i];
  131.             auto src_file = file.filepath;
  132.             auto reletive_path = src_file.substr(srcdir.str().length() + 1);
  133.             auto dstpath = dstdir.join(reletive_path);
  134.             auto srcpath = cutl::path(src_file);
  135.             if (file.type == filetype::file || file.type == filetype::symlink)
  136.             {
  137.                 if (!copyfile(srcpath, dstpath, true))
  138.                 {
  139.                     return false;
  140.                 }
  141.             }
  142.             else if (file.type == filetype::directory)
  143.             {
  144.                 if (!createdir(dstpath, true))
  145.                 {
  146.                     return false;
  147.                 }
  148.                 if (!copy_attributes(src_file, dstpath.str(), true))
  149.                 {
  150.                     return false;
  151.                 }
  152.             }
  153.             else
  154.             {
  155.                 CUTL_WARN("the file cannot be copy: [" + filetype_flag(srcpath.type()) + "]" + srcpath.str());
  156.                 continue;
  157.             }
  158.         }
  159.         return true;
  160.     }
复制代码
4. filesystem_win.h
  1. #include <vector>
  2. #include <string>
  3. #pragma once
  4. namespace cutl
  5. {
  6.     // 拷贝文件/文件夹的属性,isdir参数只在windows下生效,Unix-like系统下不起作用
  7.     bool copy_attributes(const std::string &srcpath, const std::string &dstpath, bool isdir = false);
  8. } // namespace cutl
复制代码
5. filesystem_win.cpp
  1. #if defined(_WIN32) || defined(__WIN32__)
  2. #include <io.h>
  3. #include <direct.h>
  4. #include <Windows.h>
  5. #include <stdlib.h>
  6. #include "strutil.h"
  7. #include "filesystem.h"
  8. #include "logger.h"
  9. namespace cutl
  10. {
  11.     bool copy_attributes(const std::string &srcpath, const std::string &dstpath, bool isdir)
  12.     {
  13.         // 获取 修改访问、修改时间
  14.         FILETIME t_create, t_access, t_write;
  15.         HANDLE h_src = NULL;
  16.         if (isdir)
  17.         {
  18.             // 文件夹
  19.             h_src = CreateFile(
  20.                 srcpath.c_str(),
  21.                 GENERIC_READ,
  22.                 FILE_SHARE_READ | FILE_SHARE_DELETE,
  23.                 NULL,
  24.                 OPEN_EXISTING,
  25.                 FILE_FLAG_BACKUP_SEMANTICS,
  26.                 NULL);
  27.         }
  28.         else
  29.         {
  30.             h_src = CreateFileA(
  31.                 srcpath.c_str(),
  32.                 GENERIC_READ,
  33.                 0,
  34.                 NULL,
  35.                 OPEN_EXISTING,
  36.                 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
  37.                 NULL);
  38.         }
  39.         if (h_src == INVALID_HANDLE_VALUE)
  40.         {
  41.             CUTL_ERROR("Failed to open file " + srcpath + ", error code: " + std::to_string(GetLastError()));
  42.             CloseHandle(h_src);
  43.             return false;
  44.         }
  45.         if (!GetFileTime(h_src, &t_create, &t_access, &t_write))
  46.         {
  47.             CUTL_ERROR("Failed to get file times for " + srcpath + ", error code: " + std::to_string(GetLastError()));
  48.             CloseHandle(h_src);
  49.             return false;
  50.         }
  51.         CloseHandle(h_src);
  52.         // 设置 修改访问、修改时间
  53.         HANDLE h_dst = NULL;
  54.         if (isdir)
  55.         {
  56.             h_dst = CreateFile(
  57.                 dstpath.c_str(),
  58.                 GENERIC_READ | GENERIC_WRITE,
  59.                 FILE_SHARE_READ | FILE_SHARE_DELETE,
  60.                 NULL,
  61.                 OPEN_EXISTING,
  62.                 FILE_FLAG_BACKUP_SEMANTICS,
  63.                 NULL);
  64.         }
  65.         else
  66.         {
  67.             h_dst = ::CreateFileA(
  68.                 dstpath.c_str(),
  69.                 // GENERIC_WRITE,
  70.                 // GENERIC_READ | GENERIC_WRITE,
  71.                 GENERIC_ALL,
  72.                 0,
  73.                 NULL,
  74.                 OPEN_EXISTING,
  75.                 FILE_ATTRIBUTE_NORMAL,
  76.                 NULL);
  77.         }
  78.         if (h_dst == INVALID_HANDLE_VALUE)
  79.         {
  80.             CUTL_ERROR("Failed to open file " + dstpath + ", error code: " + std::to_string(GetLastError()));
  81.             CloseHandle(h_dst);
  82.             return false;
  83.         }
  84.         if (!SetFileTime(h_dst, &t_create, &t_access, &t_write))
  85.         {
  86.             CUTL_ERROR("Failed to set file times for " + dstpath + ", error code: " + std::to_string(GetLastError()));
  87.             CloseHandle(h_dst);
  88.             return false;
  89.         }
  90.         CloseHandle(h_dst);
  91.         // 修改文件属性
  92.         // 注意:文件访问权限的属性设置要放在文件时间的修改后面,因为只读权限的文件不允许修改时间
  93.         DWORD attributes = GetFileAttributesA(srcpath.c_str());
  94.         if (attributes == INVALID_FILE_ATTRIBUTES)
  95.         {
  96.             CUTL_ERROR("Failed to get file attributes for " + srcpath + ", error code: " + std::to_string(GetLastError()));
  97.             return false;
  98.         }
  99.         if (!SetFileAttributesA(dstpath.c_str(), attributes))
  100.         {
  101.             CUTL_ERROR("Failed to set file attributes for " + dstpath + ", error code: " + std::to_string(GetLastError()));
  102.             return false;
  103.         }
  104.         return true;
  105.     }
  106. } // namespace cutl
  107. #endif // defined(_WIN32) || defined(__WIN32__)
复制代码
6. filesystem_unix.cpp
  1. #if defined(_WIN32) || defined(__WIN32__)
  2. // do nothing
  3. #else
  4. #include <unistd.h>
  5. #include <sys/stat.h>
  6. #include <dirent.h>
  7. #include <stack>
  8. #include <cstring>
  9. #include <utime.h>
  10. #include <stdlib.h>
  11. #include <sys/time.h>
  12. #include "filesystem.h"
  13. #include "inner/logger.h"
  14. namespace cutl
  15. {
  16.     bool copy_attributes(const std::string &srcpath, const std::string &dstpath, bool isdir)
  17.     {
  18.         struct stat attr_of_src;
  19.         int ret = lstat(srcpath.c_str(), &attr_of_src);
  20.         if (ret != 0)
  21.         {
  22.             CUTL_ERROR("lstat error. srcpath:" + srcpath + ", error:" + strerror(errno));
  23.             return false;
  24.         }
  25.         // 修改文件属性
  26.         ret = chmod(dstpath.c_str(), attr_of_src.st_mode);
  27.         if (ret != 0)
  28.         {
  29.             CUTL_ERROR("chmod error. dstpath:" + dstpath + ", error:" + strerror(errno));
  30.             return false;
  31.         }
  32.         // 修改文件用户组
  33.         ret = chown(dstpath.c_str(), attr_of_src.st_uid, attr_of_src.st_gid);
  34.         if (ret != 0)
  35.         {
  36.             CUTL_ERROR("chown error. dstpath:" + dstpath + ", error:" + strerror(errno));
  37.             return false;
  38.         }
  39.         // 修改文件访问、修改时间
  40.         if (S_ISLNK(attr_of_src.st_mode))
  41.         {
  42.             // TODO: 编译还有问题,需要确定编译宏
  43.             // struct timeval time_buf[2];
  44.             // time_buf[0].tv_sec = attr_of_src.st_atim.tv_sec;
  45.             // time_buf[0].tv_usec = attr_of_src.st_atim.tv_nsec / 1000;
  46.             // time_buf[1].tv_sec = attr_of_src.st_mtim.tv_sec;
  47.             // time_buf[1].tv_usec = attr_of_src.st_mtim.tv_nsec / 1000;
  48.             // ret = lutimes(dstpath.c_str(), time_buf);
  49.             // if (ret != 0)
  50.             // {
  51.             //     CUTL_ERROR("lutimes error. dstpath:" + dstpath + ", error:" + strerror(errno));
  52.             //     return false;
  53.             // }
  54.         }
  55.         else
  56.         {
  57.             struct utimbuf tbuf;
  58.             tbuf.actime = attr_of_src.st_atime;
  59.             tbuf.modtime = attr_of_src.st_mtime;
  60.             ret = utime(dstpath.c_str(), &tbuf);
  61.             if (ret != 0)
  62.             {
  63.                 CUTL_ERROR("utime error. dstpath:" + dstpath + ", error:" + strerror(errno));
  64.                 return false;
  65.             }
  66.         }
  67.         return true;
  68.     }
  69. } // namespace cutl
  70. #endif // defined(_WIN32) || defined(__WIN32__)
复制代码
7. 源码地点

更多详细代码,请查看本人写的C++ 通用工具库: common_util, 本项目已开源,代码简便,且有详细的文档和Demo。
【SunLogging】
扫码二维码,关注微信公众号,阅读更多精彩内容
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4