c++實現文件複製並修改相應屬性

来源:https://www.cnblogs.com/harrypotterjackson/archive/2019/12/14/12041311.html
-Advertisement-
Play Games

問題描述 完成一個目錄複製命令mycp,包括目錄下的文件和子目錄, 運行結果如下: 思路 這道題目主要涉及文件讀寫操作和屬性修改。需要支持文件夾複製、文件複製,在Linux下還要支持軟鏈接的複製。 思路如下: 獲取待複製目錄的絕對路徑 根據絕對路徑進行dfs或者bfs搜索所有子目錄項 判斷子目錄是屬 ...


問題描述

完成一個目錄複製命令mycp,包括目錄下的文件和子目錄, 運行結果如下:

[email protected] [~/]# ls –la sem

total 56

drwxr-xr-x  3 beta beta 4096 Dec 19 02:53 ./

drwxr-xr-x  8 beta beta 4096 Nov 27 08:49 ../

-rw-r--r--  1 beta beta  128 Nov 27 09:31 Makefile

-rwxr-xr-x  1 beta beta 5705 Nov 27 08:50 consumer*

-rw-r--r--  1 beta beta  349 Nov 27 09:30 consumer.c

drwxr-xr-x  2 beta beta 4096 Dec 19 02:53 subdir/

[email protected] [~/]# mycp sem target

[email protected] [~/]# ls –la target

total 56

drwxr-xr-x  3 beta beta 4096 Dec 19 02:53 ./

drwxr-xr-x  8 beta beta 4096 Nov 27 08:49 ../

-rw-r--r--  1 beta beta  128 Nov 27 09:31 Makefile

-rwxr-xr-x  1 beta beta 5705 Nov 27 08:50 consumer*

-rw-r--r--  1 beta beta  349 Nov 27 09:30 consumer.c

drwxr-xr-x  2 beta beta 4096 Dec 19 02:53 subdir/

思路

這道題目主要涉及文件讀寫操作和屬性修改。需要支持文件夾複製、文件複製,在Linux下還要支持軟鏈接的複製。

思路如下:

  • 獲取待複製目錄的絕對路徑
  • 根據絕對路徑進行dfs或者bfs搜索所有子目錄項
  • 判斷子目錄是屬於什麼類型:文件夾、普通文件、軟鏈接
  • 分別對三種(Windows下只有文件夾和文件)進行複製操作
  • 修改目標文件屬性與源文件保持一致

使用到的函數主要有:

Linux

判斷文件類型

  1. int lstat(const char *pathname, struct stat *statbuf);

    • const char *pathname 需要判斷的文件的路徑
    • struct stat *statbuf 用於保存文件屬性
    • return int 0成功,-1失敗
  2. 判斷

    文件類型 說明 判斷函數 例子
    普通文件 一般意義上的文件 S_ISREG() hello.c
    目錄文件 可包含其他文件或者目錄 S_ISDIR() /etc/
    字元設備文件 以無緩衝方式,提供對於設備的可變長度訪問 S_ISCHR() /dev/tty
    塊設備文件 以帶緩衝方式,提供對於設備的固定長度訪問 S_ISBLK() /dev/sda
    符號鏈接文件 指向另一個文件 S_ISLNK() /dev/cdrom
    命名管道文件 用於進程間通信 S_ISFIFO() /dev/inictl
    網路套接字文件 用於進程間的網路通信 S_ISSOCK() /dev/log

    值得註意的是,需要先判斷是不是符號鏈接文件。對符號鏈接文件進行S_ISREG()判斷時會出現將其判斷為普通文件的情況。可能是由於他判斷的是鏈接文件所指向的文件的類型。因此需要先判斷是不是鏈接文件。

遍歷文件目錄

  1. DIR *opendir(const char *name);

    • const char *name 待打開的目錄路徑
    • return DIR 返回目錄數據結構
  2. struct dirent *readdir(DIR *dirp);

    • ```DIR *dirp 待讀取的目錄

    • return struct dirent* 返回順序讀取的該目錄下的目錄項

      註意,第一個目錄項是., 第二個目錄項是..

複製文件

  1. int open(const char *pathname, int flags, mode_t mode);
    • const char* pathname 待打開的文件路徑
    • int flags O_RDONLY, O_WRONLY, or O_RDWR
    • mode_t mode
    • return int 返回值是打開的文件描述符
  2. int creat(const char *pathname, mode_t mode);
    • const char* pathname 待創建的文件名(可以包含路徑)
    • mode_t mode 創建屬性
    • return int 待創建文件描述符
  3. ssize_t read(int fd, void *buf, size_t count);
    • int fd 待讀取的文件的描述符
    • void* buf 讀取的內容存放緩衝區。儘管這裡是void*buf在創建的時候應該是```char*``````
    • ​ ```size_t count 要讀取的內容的大小。如果大於fd中的數目,則讀取相應大小內容
    • return ssize_t 返回實際讀取的內容的數目。失敗返回-1
  4. ssize_t write(int fd, const void *buf, size_t count);
    • int fd 要寫入的文件的描述符
    • const void *buf 要寫入的內容。
    • size_t count 要寫入的內容大小
    • return ssize_t 成功返回實際寫入的數目。失敗返回-1

複製文件夾

複製文件夾就是創建同名文件夾

  1. int mkdir(const char *pathname, mode_t mode);
    • const char* pathname 待創建的目錄項的地址
    • mode_t mode 創建目錄項的屬性
    • return int 如果成功返回為0,否則返回-1

複製軟鏈接

  1. ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);
    • const cha* pathname 待讀取軟鏈接的路徑
    • char* buf 軟鏈接中的內容保存到buf
    • size_t bufsiz buf緩衝區的大小
    • return ssize_t 返回值是軟鏈接指向路徑的長度
  2. int symlink(const char *target, const char *linkpath);
    • const char* target 待創建的軟鏈接的路徑
    • const char* linkpath 待創建的軟鏈接所指向的路徑
    • return int 成功返回0,否則返回-1

獲取屬性

  1. int lstat(const char *pathname, struct stat *statbuf);

    • const char *pathname 需要提取屬性的文件或者文件夾的路徑
    • struct stat *statbuf 獲取到的屬性存儲緩衝區
    • return int 成功返回0,否則-1
  2. int chmod(const char *pathname, mode_t mode);

    • const char *pathname 待修改屬性的文件的路徑
    • mode_t mode 將待修改文件修改改為該屬性
    • return int 若成功返回0,否則返回-1
  3. int chown(const char *pathname, uid_t owner, gid_t group);

    • const char* pathname 待更改的目錄路徑
    • uid_t owner 如果是-1則不改變屬性
    • gid_t group 如果是-1則不改變屬性
  4. int lutimes(const char *filename, const struct timeval tv[2]);

    這個命令用於修改目標文件的access_time modify_timelutimes可以修改軟鏈接的屬性。

    • const char *filename 待修改的文件路徑
    • const struct timeval tv[2] tv[0]access_timetv[1]modify_time
    • return int 如果成功返回0,否則返回-1

Windows

這裡主要列出所用到的函數,有需要的話可以詳細去查相關的函數說明:

FindFirstFile

FindNextFile

CreateFile

GetFileSize

ReadFile

WriteFile

_wmkdir

GetFileAttributes

SetFileAttributes

GetFileTime

FileTimeToSystemTime

SetFileTime

源代碼實現

Linux

#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <utime.h>
#include <time.h> 
#include <sys/time.h>
//文件夾複製採用dfs或者bfs進行搜索是等價的
void search_dfs(char *src_path, char *dest_path);
//複製文件
void copy_file(const char *src_file, const char *dst_file);
//複製文件夾
void copy_dir(const char *src_dir, const char *dst_dir);
//複製軟鏈接
void copy_sln(const char *src_file, const char *dst_file);
//修改文件屬性與源文件保持一致
void changeAttr(const char *src, const char *dst);
//修改路徑
void change_path(char *src, char *cat)
{
    strcat(src, (char *)"/");
    strcat(src, cat);
}

int main(int argc, char const *argv[])
{
    if (argc < 3)
    {
        printf("No file or directory specified\n");
        exit(-1);
    }
    if (argc > 3)
    {
        printf("Too many arguments\n");
        exit(-1);
    }

    char src[1024], dest[1024];
    char *current_dir = getcwd(NULL, 0);

    struct stat state_of_entry;
    lstat(argv[1], &state_of_entry);
    if (S_ISDIR(state_of_entry.st_mode)) //目錄
    {
        if (chdir(argv[1])) //目錄錯誤
        {
            perror("chdir");
            exit(-1);
        }
        strcpy(src, getcwd(NULL, 0)); //獲取源文件夾絕對路徑
        chdir(current_dir);
        lstat(argv[2], &state_of_entry);
        if (S_ISDIR(state_of_entry.st_mode)) //目錄
        {
            if (chdir(argv[2])) //目錄錯誤
            {
                perror("chdir");
                exit(-1);
            }
            strcpy(dest, getcwd(NULL, 0)); //獲取目標文件夾絕對路徑
            chdir(current_dir);
            chdir(dest);
            // printf("SRC: %s\n", src);
            // printf("DEST: %s\n", dest);
            chdir(src);
            search_dfs(src, dest);
        }
        else
        {
            printf("error. No destination directory.\n");
            exit(-1);
        }
    }

    else //文件直接複製
    {
        char dest[1024];
        lstat(argv[2], &state_of_entry);
        if (S_ISDIR(state_of_entry.st_mode)) //目錄
        {
            strcpy(dest, getcwd(NULL, 0)); //獲取目標文件夾絕對路徑
        }
        else
        {
            strcpy(dest, "./");
            strcat(dest, argv[2]);
        }
        copy_file(argv[1], argv[2]);
    }

    return 0;
}

void search_dfs(char *src_path, char *dest_path)
{
    // printf("Searching directory:    %s\n", getcwd(NULL, 0));
    DIR *src_dir = opendir(src_path);
    DIR *dest_dir = opendir(dest_path);
    struct dirent *entry = NULL;
    struct stat state_of_entry;
    while ((entry = readdir(src_dir)) != NULL)
    {
        lstat(entry->d_name, &state_of_entry);
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
            continue;
        // printf("entry->d_name: %s\n", entry->d_name);
        if (S_ISLNK(state_of_entry.st_mode)) //符號鏈接
        {
            char src_file[1024];
            char dest_file[1024];
            strcpy(src_file, src_path);
            change_path(src_file, entry->d_name);
            strcpy(dest_file, dest_path);
            change_path(dest_file, entry->d_name);
            // printf("src file: %s\n", src_file);
            // printf("dest file: %s\n", dest_file);
            copy_sln(src_file, dest_file);
        }
        else if (S_ISREG(state_of_entry.st_mode)) //普通文件
        {
            char src_file[1024];
            char dest_file[1024];
            strcpy(src_file, src_path);
            change_path(src_file, entry->d_name);
            strcpy(dest_file, dest_path);
            change_path(dest_file, entry->d_name);
            // printf("src file: %s\n", src_file);
            // printf("dest file: %s\n", dest_file);
            copy_file(src_file, dest_file);
        }
        else if (S_ISDIR(state_of_entry.st_mode)) //目錄
        {
            char src[1024];
            char dest[1024];
            strcpy(src, src_path);
            change_path(src, entry->d_name);
            strcpy(dest, dest_path);
            change_path(dest, entry->d_name);
            // printf("src dir: %s\n", src);
            // printf("dest dir: %s\n", dest);
            copy_dir(src, dest);
            search_dfs(src, dest);
        }
    }
}

void copy_file(const char *src_file, const char *dest_file)
{
    int src_fd = open(src_file, O_RDONLY);
    int dest_fd = creat(dest_file, O_WRONLY);

    unsigned char buf[1024];
    while (read(src_fd, buf, sizeof(buf)) > 0)
    {
        write(dest_fd, buf, sizeof(buf));
    }

    changeAttr(src_file, dest_file);

    close(src_fd);
    close(dest_fd);
}

void copy_dir(const char *src_dir, const char *dst_dir)
{
    mkdir(dst_dir, 0777);
    changeAttr(src_dir, dst_dir);
}

void copy_sln(const char *src_file, const char *dst_file)
{
    char buf[1024];
    memset(buf, 0, sizeof(buf));
    int len = 0;
    if ((len = readlink(src_file, buf, sizeof(buf))) > 0)
    {
        printf("%s\n", buf);
        if (symlink(buf, dst_file) == -1)
        {
            perror("symlink");
        }
    }
    changeAttr(src_file, dst_file);
}

void changeAttr(const char *src, const char *dst)
{
    struct stat attr_of_src;
    lstat(src, &attr_of_src);
    //修改文件屬性
    chmod(dst, attr_of_src.st_mode);
    //修改文件用戶組
    chown(dst, attr_of_src.st_uid, attr_of_src.st_gid);

    //修改文件訪問、修改時間
    struct timeval time_buf[2];
    time_buf[1].tv_sec = attr_of_src.st_mtime;
    time_buf[0].tv_sec = attr_of_src.st_atime;
    if(lutimes(dst, time_buf) == -1)
    {
        printf("%s\n", dst);
        perror("lutimes");
    }

    struct utimbuf tbuf;
    tbuf.actime = attr_of_src.st_atime;
    tbuf.modtime = attr_of_src.st_mtime;
    utime(dst, &tbuf);

    struct stat dst_attr_of_src;
    lstat(dst, &dst_attr_of_src);
    if (dst_attr_of_src.st_mtime != attr_of_src.st_mtime)
        printf("%s : %d\n", dst, attr_of_src.st_mtime);
}

Windows

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <Windows.h>
#include <cstring>
#define BUF_SIZE 1024
using namespace std;

//文件夾複製採用dfs或者bfs進行搜索是等價的
void search_dfs(wchar_t* src_path, wchar_t* dest_path);
//複製文件
void copy_file(const wchar_t* src_file, const wchar_t* dst_file);
//複製文件夾
void copy_dir(const wchar_t* src_dir, const wchar_t* dst_dir);
//修改文件屬性與源文件保持一致
void change_attr(const wchar_t* src_name, const wchar_t* dst_name, HANDLE h_src, HANDLE h_dst);

int wmain(int argc, wchar_t* argv[])
{
    setlocale(LC_ALL, "");
    wchar_t* src_path = new wchar_t[BUF_SIZE];
    wchar_t* dst_path = new wchar_t[BUF_SIZE];
    ZeroMemory(src_path, BUF_SIZE);
    ZeroMemory(dst_path, BUF_SIZE);
    wcscpy(src_path, argv[1]);
    wcscat(src_path, L"\\*");
    wcscpy(dst_path, argv[2]);
    wcout << L"src_path" << src_path << endl;
    wcout << L"dst_path" << dst_path << endl;
    //wcscpy(src_path, L"E:\\薪火培訓\\*");
    //wcscpy(dst_path, L"E:\\2333");
    search_dfs(src_path, dst_path);
    delete[] src_path;
    delete[] dst_path;
    return 0;
}

//複製文件
void copy_file(const wchar_t* src_file, const wchar_t* dst_file)
{
    HANDLE h_src = ::CreateFile(
            src_file, 
            GENERIC_READ | GENERIC_WRITE, 
            0, 
            NULL, 
            OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, 
            NULL);
    HANDLE h_dst = ::CreateFile(
            dst_file,
            GENERIC_WRITE | GENERIC_READ,
            0,
            NULL,
            CREATE_ALWAYS,
            FILE_ATTRIBUTE_NORMAL,
            NULL);
    if (h_src == INVALID_HANDLE_VALUE || h_dst == INVALID_HANDLE_VALUE)
    {
        printf("Open file error!\n");
        CloseHandle(h_src);
        CloseHandle(h_dst);
        exit(-1);
    }
    DWORD all_bytes = GetFileSize(h_src, NULL);
    char* buffer = new char[all_bytes + 1];
    ZeroMemory(buffer, all_bytes + 1);

    DWORD bytes = 0;
    ReadFile(h_src, buffer, all_bytes, &bytes, NULL);
    WriteFile(h_dst, buffer, all_bytes, &bytes, NULL);

    change_attr(src_file, dst_file, h_src, h_dst);

    CloseHandle(h_src);
    CloseHandle(h_dst);
    delete[] buffer;
    return;
}

//複製文件夾
void copy_dir(const wchar_t* src_dirr, const wchar_t* dst_dir)
{
    wchar_t* src_dir = new wchar_t[BUF_SIZE];
    wcscpy(src_dir, src_dirr);
    src_dir[wcslen(src_dir) - 2] = '\0';
    _wmkdir(dst_dir);
    HANDLE h_src = CreateFile(
        src_dir,
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_DELETE,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_BACKUP_SEMANTICS,
        NULL
    );
    if (h_src == INVALID_HANDLE_VALUE)
    {
        printf("Open src directory error!\n");
        CloseHandle(h_src);
        exit(-1);
    }
    HANDLE h_dst = CreateFile(
        dst_dir,
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_DELETE,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_BACKUP_SEMANTICS,
        NULL
    );
    
    if (h_dst == INVALID_HANDLE_VALUE)
    {
        printf("Open dst directory error!\n");
        CloseHandle(h_dst);
        exit(-1);
    }
    change_attr(src_dir, dst_dir, h_src, h_dst);
    CloseHandle(h_src);
    CloseHandle(h_dst);
    return;
}


void search_dfs(wchar_t* src_path, wchar_t* dst_path)
{
    HANDLE h_find;
    WIN32_FIND_DATA find_data;
    LARGE_INTEGER size;
    h_find = FindFirstFile(src_path, &find_data);
    if (h_find == INVALID_HANDLE_VALUE)
    {
        cout << "Fail to find first file" << endl;
        return;
    }
    copy_dir(src_path, dst_path);
    do
    {
        wcout << find_data.cFileName << endl;
        if (wcscmp(find_data.cFileName, L".") == 0 || wcscmp(find_data.cFileName, L"..") == 0)
        {
            continue;
        }
        if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)//是目錄
        {
            wchar_t n_src_path[BUF_SIZE];
            wcscpy(n_src_path, src_path);
            n_src_path[wcslen(n_src_path) - 1] = '\0';
            wcscat(n_src_path, find_data.cFileName);
            wcscat(n_src_path, L"\\*");

            wchar_t n_dst_path[BUF_SIZE];
            wcscpy(n_dst_path, dst_path);
            wcscat(n_dst_path, L"\\");
            wcscat(n_dst_path, find_data.cFileName);

            copy_dir(n_src_path, n_dst_path);
            search_dfs(n_src_path, n_dst_path);
        }
        else
        {
            //size.LowPart = find_data.nFileSizeLow;
            //size.HighPart = find_data.nFileSizeHigh;
            //wcout << find_data.cFileName << "\t" << size.QuadPart << " bytes" << endl;
            
            wchar_t n_src_path[BUF_SIZE];
            wcscpy(n_src_path, src_path);
            n_src_path[wcslen(n_src_path) - 1] = '\0';
            wcscat(n_src_path, find_data.cFileName);

            wchar_t n_dst_path[BUF_SIZE];
            wcscpy(n_dst_path, dst_path);
            wcscat(n_dst_path, L"\\");
            wcscat(n_dst_path, find_data.cFileName);

            copy_file(n_src_path, n_dst_path);
        }

    } while (FindNextFile(h_find, &find_data));
    return;
}

//修改文件屬性與源文件保持一致
void change_attr(const wchar_t* src_name, const wchar_t* dst_name, HANDLE h_src, HANDLE h_dst)
{
    DWORD attr = GetFileAttributes(src_name);
    SetFileAttributes(dst_name, attr);
    FILETIME t_create, t_access, t_write;
    SYSTEMTIME syst_create, syst_access, syst_write;
    GetFileTime(h_src, &t_create, &t_access, &t_write);

    FileTimeToSystemTime(&t_create, &syst_create);
    //cout << syst_create.wDay << endl;

    SetFileTime(h_dst, &t_create, &t_access, &t_write);

    GetFileTime(h_dst, &t_create, &t_access, &t_write);
    FileTimeToSystemTime(&t_create, &syst_create);
    //cout << syst_create.wDay << endl;
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 舉個慄子 路邊攤吃燒烤,老闆只有一人,又要記住點單,又要忙著收費,客戶做什麼都要排隊。。。 反例思路1 如果客戶多了,請求多,就容易混亂,他叫五串羊肉串,你叫三個烤雞翅,老闆哪裡忙得過來。代碼中的體現就是一句一句輸出,所以應該要有個服務員。。。 門面改造 Talk is cheap, show me ...
  • 舉個慄子 在M和N兩種品牌的手機裡面各做通訊錄和游戲兩種軟體。前提:M和N手機之間不通用。 反例思路1 以手機品牌為主體,各自實現軟體。 反例思路2 以手機軟體為主體,各自適配品牌。 弊端說明 對象的繼承關係是在編譯時就定義好了,所以無法在運行時改變從父類繼承的實現。 子類的實現與它的父類有非常緊密 ...
  • 想象一下你馬上出發要去一家餐廳吃飯,但是你去之前不確定會不會滿桌,你又不想排號。這時的你會有兩個選擇,如果你是個樂觀的人,內心戲可能會是「管他的,去了再說,大不了沒座就回來」。反之,如果你是一個悲觀的人,可能會先打個電話預約一下,先確認下肯定有座,同時交點定金讓餐廳預留好這個座位,這樣就可以直接去了 ...
  • 背景 薩班斯(SOX)法案。在美國上市公司必須遵循的“薩班斯(SOX)法案” 中要求對企業內部網路信息系統進行評估,其中涉及對業務系統操作、資料庫訪問等業務行為的審計。 日誌審計模型系統架構參考四層模型日誌審計類別1) HTTP 會話審計從流量中還原 HTTP 會話數據,並根據會話特征進一步深度解析... ...
  • 說點閑話 距離上次寫博客,已經有一年了。在動手寫之前,總是帶著深深的罪惡感。被它折磨許久,終於,還是,動手了。 值得慶祝的一件事:最近開始健身了。每天動感單車45分鐘,游泳45分鐘,真的是(生)爽(不)到(如)爆(死)。 好了,扯淡完畢,步入正題。 ActiveRecord被莫名寫入? 準備知識 代 ...
  • 做了3天的win10的相容性測試,大部分時間都卡許可權獲取這了。 以下廢話很多,想直接找解決方法,請跳至紅字 首先,簡單說下uac,自vista後windows再次加嚴了許可權管理,uac (賬戶控制) ,就是程式對訪問一些敏感資源時的限制,當程式需要訪問限制資源時會彈窗讓用戶選擇。現在系統主要分兩種權 ...
  • LocalDate、LocalTime、Instant、Duration、Period 1.1使用LocalDate和LocalTime 1.1.1LocalDate的創建方式和相關方法使用示例 1.1.1LocalTime對象的創建方式以及相關方法示例 1.1.3LocalDateTime對象3種 ...
  • 定義:一種可以實現“ 先進後出 ”的存儲結構 分類: 1. 靜態棧 2. 動態棧 演算法: 1. 出棧 2. 壓棧 代碼實現: 多敲,多敲 ,後期改進 應用: 1. 函數調用 2. 中斷 3. 表達式求值 4. 記憶體分配 5. 緩衝處理 6. 迷宮 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...