1. 关键词
C++ 文件系统操作 拷贝文件 拷贝文件夹 跨平台
2. fileutil.h
- #pragma once
- #include <string>
- #include <cstdio>
- #include <cstdint>
- #include "filetype.h"
- #include "filepath.h"
- namespace cutl
- {
- /**
- * @brief The file guard class to manage the FILE pointer automatically.
- * file_guard object can close the FILE pointer automatically when his scope is exit.
- */
- class file_guard
- {
- public:
- /**
- * @brief Construct a new file guard object
- *
- * @param file the pointer of the FILE object
- */
- explicit file_guard(FILE *file);
- /**
- * @brief Destroy the file guard object
- *
- */
- ~file_guard();
- /**
- * @brief Get the FILE pointer.
- *
- * @return FILE*
- */
- FILE *getfd() const;
- private:
- FILE *file_;
- };
- /**
- * @brief Copy a file or symbolic link(shortcut on windows).
- *
- * @param srcpath the filepath of the source file or symbolic link to be copied
- * @param dstpath the filepath of the destination file or symbolic link to be copied to
- * @param attributes whether to copy the file attributes, default is false.
- * If true, means copy the file attributes, like the 'cp -p' command.
- * @return true if the file or symbolic link is copied successfully, false otherwise.
- * @note If the destination file or directory already exists, it will be overwritten.
- * @return false
- */
- bool copyfile(const filepath &srcpath, const filepath &dstpath, bool attributes = false);
- // copy directory recursively
- /**
- * @brief Copy a directory recursively.
- *
- * @param srcdir the filepath of the source directory to be copied
- * @param dstdir the filepath of the destination directory to be copied to
- * @return true if the directory is copied successfully, false otherwise.
- * @note If the destination directory already exists, it will be overwritten.
- */
- bool copydir(const filepath &srcdir, const filepath &dstdir);
- } // namespace cutl
复制代码 3. fileutil.cpp
- #include <cstdio>
- #include <map>
- #include <iostream>
- #include <cstring>
- #include <sys/stat.h>
- #include "fileutil.h"
- #include "inner/logger.h"
- #include "inner/filesystem.h"
- #include "strutil.h"
- namespace cutl
- {
- file_guard::file_guard(FILE *file)
- : file_(file)
- {
- }
- file_guard::~file_guard()
- {
- if (file_)
- {
- // CUTL_DEBUG("close file");
- int ret = fclose(file_);
- if (ret != 0)
- {
- CUTL_ERROR("fail to close file, ret" + std::to_string(ret));
- }
- file_ = nullptr;
- }
- // ROBOLOG_DCHECK(file_ == nullptr);
- }
- FILE *file_guard::getfd() const
- {
- return file_;
- }
- bool copyfile(const filepath &srcpath, const filepath &dstpath, bool attributes)
- {
- // CUTL_INFO("file type: " + std::to_string(srcpath.type()) + ", " + filetype_flag(srcpath.type()) + ", " + srcpath.str() + ", dstpath:" + dstpath.str());
- // copy file content
- if (srcpath.isfile())
- {
- if (dstpath.exists())
- {
- // remove if exists
- removefile(dstpath);
- }
- file_guard frd(fopen(srcpath.str().c_str(), "rb"));
- if (frd.getfd() == nullptr)
- {
- CUTL_ERROR("open file failed, " + srcpath.str());
- return false;
- }
- file_guard fwt(fopen(dstpath.str().c_str(), "wb"));
- if (fwt.getfd() == nullptr)
- {
- CUTL_ERROR("open file failed, " + dstpath.str());
- return false;
- }
- static constexpr size_t buf_size = 8 * 1024;
- uint8_t buffer[buf_size] = {0};
- size_t read_len = 0;
- size_t write_len = 0;
- while ((read_len = fread(buffer, 1, buf_size, frd.getfd())) > 0)
- {
- write_len = fwrite(buffer, 1, read_len, fwt.getfd());
- if (write_len != read_len)
- {
- CUTL_ERROR("write file failed, only write " + std::to_string(write_len) + ", read_len:" + std::to_string(read_len));
- return false;
- }
- }
- // flush file to disk
- int ret = fflush(fwt.getfd());
- if (0 != ret)
- {
- CUTL_ERROR("fail to flush file:" + dstpath.str());
- return false;
- }
- if (!file_sync(fwt.getfd()))
- {
- CUTL_ERROR("file_sync failed for " + dstpath.str());
- return false;
- }
- }
- else if (srcpath.issymlink())
- {
- if (dstpath.exists())
- {
- // remove if exists
- file_removelink(dstpath.str());
- }
- auto link_path = file_readlink(srcpath.str());
- if (link_path.empty())
- {
- CUTL_ERROR("readlink failed for " + srcpath.str());
- return false;
- }
- if (!file_createlink(link_path, dstpath.str()))
- {
- CUTL_ERROR("createlink failed for " + dstpath.str());
- return false;
- }
- }
- else
- {
- CUTL_ERROR("not a file or symlink, cannot copy: [" + filetype_flag(srcpath.type()) + "]" + srcpath.str());
- return false;
- }
- // copy file attributes
- if (attributes && srcpath.isfile())
- {
- return copy_attributes(srcpath.str(), dstpath.str());
- }
- return true;
- }
- // https://www.cnblogs.com/harrypotterjackson/p/12113382.html
- bool copydir(const filepath &srcdir, const filepath &dstdir)
- {
- if (!srcdir.isdir())
- {
- CUTL_ERROR("srcdir is not a directory: " + srcdir.str());
- return false;
- }
- if (!dstdir.exists() && !createdir(dstdir, true))
- {
- CUTL_ERROR("createdir failed for " + dstdir.str());
- return false;
- }
- auto filelist = list_files(srcdir, filetype::all, true);
- for (size_t i = 0; i < filelist.size(); i++)
- {
- auto file = filelist[i];
- auto src_file = file.filepath;
- auto reletive_path = src_file.substr(srcdir.str().length() + 1);
- auto dstpath = dstdir.join(reletive_path);
- auto srcpath = cutl::path(src_file);
- if (file.type == filetype::file || file.type == filetype::symlink)
- {
- if (!copyfile(srcpath, dstpath, true))
- {
- return false;
- }
- }
- else if (file.type == filetype::directory)
- {
- if (!createdir(dstpath, true))
- {
- return false;
- }
- if (!copy_attributes(src_file, dstpath.str(), true))
- {
- return false;
- }
- }
- else
- {
- CUTL_WARN("the file cannot be copy: [" + filetype_flag(srcpath.type()) + "]" + srcpath.str());
- continue;
- }
- }
- return true;
- }
复制代码 4. filesystem_win.h
- #include <vector>
- #include <string>
- #pragma once
- namespace cutl
- {
- // 拷贝文件/文件夹的属性,isdir参数只在windows下生效,Unix-like系统下不起作用
- bool copy_attributes(const std::string &srcpath, const std::string &dstpath, bool isdir = false);
- } // namespace cutl
复制代码 5. filesystem_win.cpp
- #if defined(_WIN32) || defined(__WIN32__)
- #include <io.h>
- #include <direct.h>
- #include <Windows.h>
- #include <stdlib.h>
- #include "strutil.h"
- #include "filesystem.h"
- #include "logger.h"
- namespace cutl
- {
- bool copy_attributes(const std::string &srcpath, const std::string &dstpath, bool isdir)
- {
- // 获取 修改访问、修改时间
- FILETIME t_create, t_access, t_write;
- HANDLE h_src = NULL;
- if (isdir)
- {
- // 文件夹
- h_src = CreateFile(
- srcpath.c_str(),
- GENERIC_READ,
- FILE_SHARE_READ | FILE_SHARE_DELETE,
- NULL,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS,
- NULL);
- }
- else
- {
- h_src = CreateFileA(
- srcpath.c_str(),
- GENERIC_READ,
- 0,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
- NULL);
- }
- if (h_src == INVALID_HANDLE_VALUE)
- {
- CUTL_ERROR("Failed to open file " + srcpath + ", error code: " + std::to_string(GetLastError()));
- CloseHandle(h_src);
- return false;
- }
- if (!GetFileTime(h_src, &t_create, &t_access, &t_write))
- {
- CUTL_ERROR("Failed to get file times for " + srcpath + ", error code: " + std::to_string(GetLastError()));
- CloseHandle(h_src);
- return false;
- }
- CloseHandle(h_src);
- // 设置 修改访问、修改时间
- HANDLE h_dst = NULL;
- if (isdir)
- {
- h_dst = CreateFile(
- dstpath.c_str(),
- GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_DELETE,
- NULL,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS,
- NULL);
- }
- else
- {
- h_dst = ::CreateFileA(
- dstpath.c_str(),
- // GENERIC_WRITE,
- // GENERIC_READ | GENERIC_WRITE,
- GENERIC_ALL,
- 0,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
- }
- if (h_dst == INVALID_HANDLE_VALUE)
- {
- CUTL_ERROR("Failed to open file " + dstpath + ", error code: " + std::to_string(GetLastError()));
- CloseHandle(h_dst);
- return false;
- }
- if (!SetFileTime(h_dst, &t_create, &t_access, &t_write))
- {
- CUTL_ERROR("Failed to set file times for " + dstpath + ", error code: " + std::to_string(GetLastError()));
- CloseHandle(h_dst);
- return false;
- }
- CloseHandle(h_dst);
- // 修改文件属性
- // 注意:文件访问权限的属性设置要放在文件时间的修改后面,因为只读权限的文件不允许修改时间
- DWORD attributes = GetFileAttributesA(srcpath.c_str());
- if (attributes == INVALID_FILE_ATTRIBUTES)
- {
- CUTL_ERROR("Failed to get file attributes for " + srcpath + ", error code: " + std::to_string(GetLastError()));
- return false;
- }
- if (!SetFileAttributesA(dstpath.c_str(), attributes))
- {
- CUTL_ERROR("Failed to set file attributes for " + dstpath + ", error code: " + std::to_string(GetLastError()));
- return false;
- }
- return true;
- }
- } // namespace cutl
- #endif // defined(_WIN32) || defined(__WIN32__)
复制代码 6. filesystem_unix.cpp
- #if defined(_WIN32) || defined(__WIN32__)
- // do nothing
- #else
- #include <unistd.h>
- #include <sys/stat.h>
- #include <dirent.h>
- #include <stack>
- #include <cstring>
- #include <utime.h>
- #include <stdlib.h>
- #include <sys/time.h>
- #include "filesystem.h"
- #include "inner/logger.h"
- namespace cutl
- {
- bool copy_attributes(const std::string &srcpath, const std::string &dstpath, bool isdir)
- {
- struct stat attr_of_src;
- int ret = lstat(srcpath.c_str(), &attr_of_src);
- if (ret != 0)
- {
- CUTL_ERROR("lstat error. srcpath:" + srcpath + ", error:" + strerror(errno));
- return false;
- }
- // 修改文件属性
- ret = chmod(dstpath.c_str(), attr_of_src.st_mode);
- if (ret != 0)
- {
- CUTL_ERROR("chmod error. dstpath:" + dstpath + ", error:" + strerror(errno));
- return false;
- }
- // 修改文件用户组
- ret = chown(dstpath.c_str(), attr_of_src.st_uid, attr_of_src.st_gid);
- if (ret != 0)
- {
- CUTL_ERROR("chown error. dstpath:" + dstpath + ", error:" + strerror(errno));
- return false;
- }
- // 修改文件访问、修改时间
- if (S_ISLNK(attr_of_src.st_mode))
- {
- // TODO: 编译还有问题,需要确定编译宏
- // struct timeval time_buf[2];
- // time_buf[0].tv_sec = attr_of_src.st_atim.tv_sec;
- // time_buf[0].tv_usec = attr_of_src.st_atim.tv_nsec / 1000;
- // time_buf[1].tv_sec = attr_of_src.st_mtim.tv_sec;
- // time_buf[1].tv_usec = attr_of_src.st_mtim.tv_nsec / 1000;
- // ret = lutimes(dstpath.c_str(), time_buf);
- // if (ret != 0)
- // {
- // CUTL_ERROR("lutimes error. dstpath:" + dstpath + ", error:" + strerror(errno));
- // return false;
- // }
- }
- else
- {
- struct utimbuf tbuf;
- tbuf.actime = attr_of_src.st_atime;
- tbuf.modtime = attr_of_src.st_mtime;
- ret = utime(dstpath.c_str(), &tbuf);
- if (ret != 0)
- {
- CUTL_ERROR("utime error. dstpath:" + dstpath + ", error:" + strerror(errno));
- return false;
- }
- }
- return true;
- }
- } // namespace cutl
- #endif // defined(_WIN32) || defined(__WIN32__)
复制代码 7. 源码地点
更多详细代码,请查看本人写的C++ 通用工具库: common_util, 本项目已开源,代码简便,且有详细的文档和Demo。
【SunLogging】扫码二维码,关注微信公众号,阅读更多精彩内容
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |