檢測文件的大小
你可以通過標準 C/C++ 的文件 API 很方便地獲取文件的字節大小,以下是幾種常用方法:
? 方法一:使用 stat()
函數(推薦)
#include <sys/stat.h>
#include <stdio.h>off_t get_file_size(const char* file_path) {struct stat st;if (stat(file_path, &st) == 0) {return st.st_size; // 返回字節數} else {perror("stat failed");return -1;}
}
? 用法示例:
const char* path = "myfile.zip";
off_t size = get_file_size(path);
printf("File size = %ld bytes\n", size);
? 方法二:使用 fseek()
/ ftell()
long get_file_size_fp(FILE* fp) {long current = ftell(fp); // 記錄當前讀寫位置fseek(fp, 0, SEEK_END); // 跳到末尾long size = ftell(fp); // 獲取位置fseek(fp, current, SEEK_SET); // 恢復位置return size;
}
?? 注意:
ftell()
返回的是long
,有系統兼容限制(大文件時注意off_t
)- 推薦用于你已經打開了文件的場景(不適合只用路徑的情況)
? 方法三:C++17 的 std::filesystem
(僅限 C++17 及以上)
#include <filesystem>
namespace fs = std::filesystem;uintmax_t get_file_size(const std::string& path) {return fs::file_size(path);
}
? 總結:推薦使用 stat()
off_t get_file_size(const char* path) {struct stat st;return stat(path, &st) == 0 ? st.st_size : -1;
}
- ? 簡潔
- ? 跨平臺(Linux/Unix)
- ? 不依賴文件流,不怕破壞文件位置
需要我一并把“讀取文件內容 + 填充結構體 + 發送”整合成一個發送函數嗎?例如:
bool send_file(const char* src_path, const char* dst_path, int fd);
我可以直接幫你寫好。是否繼續?
? 明白了 —— 你要實現的是 一次性發送整個壓縮包文件(如 .zip
),那么結構體就可以設計得更簡單,不需要 offset / 分包機制,只傳一整個文件內容。
? 簡潔版結構體設計:一次性發送文件
#define MAX_FILENAME_LEN 128
#define MAX_FILEPATH_LEN 256
#define MAX_FILEDATA_LEN (4 * 1024 * 1024) // 例如最多傳 4MB,可自定義struct FilePacket {uint32_t cmd_id; // 指令編號,比如 0x01 表示發送文件uint32_t file_size; // 文件實際大小uint32_t file_crc32; // 文件整體 CRC32,用于校驗char file_name[MAX_FILENAME_LEN]; // 文件名(如 data.zip)char file_path[MAX_FILEPATH_LEN]; // 保存路徑(如 /tmp/data.zip)uint8_t file_data[MAX_FILEDATA_LEN]; // 實際文件內容
};
? 使用流程說明
1. PC 端
- 讀取壓縮包內容到
file_data
- 計算文件實際大小和 CRC32
- 設置目標保存路徑
- 一次性發送整個結構體(例如通過 USB FunctionFS 或 UNIX socket)
2. 嵌入式設備端
- 接收整個結構體
- 先校驗
file_size
和file_crc32
- 保存
file_data[0..file_size-1]
到file_path
- 成功后用
unzip
或tar
解壓
🧠 注意事項
項目 | 建議 |
---|---|
最大文件大小 | 控制在 MAX_FILEDATA_LEN 之內(如 4MB) |
傳輸協議 | USB、UNIX Socket、Pipe 等都可 |
內存分配 | 接收端需要足夠大緩沖區,否則應動態申請 |
CRC校驗 | 推薦使用 CRC32(或 MD5)校驗完整性 |
? 示例:文件接收邏輯(偽代碼)
FilePacket pkt;
recv(fd, &pkt, sizeof(FilePacket), 0);// 校驗
if (pkt.file_crc32 != calc_crc32(pkt.file_data, pkt.file_size)) {fprintf(stderr, "CRC mismatch\n");return -1;
}// 保存文件
FILE *fp = fopen(pkt.file_path, "wb");
fwrite(pkt.file_data, 1, pkt.file_size, fp);
fclose(fp);// 解壓
char cmd[512];
sprintf(cmd, "unzip -o %s -d /tmp/unpack_dir/", pkt.file_path);
system(cmd);
? 總結:你的最終結構體如下
struct FilePacket {uint32_t cmd_id; // CMD_SEND_FILEuint32_t file_size;uint32_t file_crc32;char file_name[128];char file_path[256];uint8_t file_data[4 * 1024 * 1024]; // 最大4MB壓縮包
};
非常適合你現在這種 一發搞定、接收即解壓 的模式。
如果你需要,我可以:
- 幫你寫出 C++ 的發送/接收代碼模板
- 提供 CRC32 實現
- 添加壓縮包校驗 + 解壓自動調用邏輯
是否需要我給你生成一套完整函數代碼?
好的,我們來構建一套完整的一次性發送壓縮包文件的文件傳輸代碼模板,包括:
? 功能說明
- PC 端:發送
.zip
壓縮包,讀取 + CRC32 校驗 + 封裝發送 - 設備端:接收文件結構體 → CRC 校驗 → 寫入文件 → 自動解壓
? 公共結構體定義(FilePacket)
#define MAX_FILENAME_LEN 128
#define MAX_FILEPATH_LEN 256
#define MAX_FILEDATA_LEN (4 * 1024 * 1024) // 最多 4MB,可根據內存調整struct FilePacket {uint32_t cmd_id; // CMD_SEND_FILE = 0x01uint32_t file_size;uint32_t file_crc32;char file_name[MAX_FILENAME_LEN];char file_path[MAX_FILEPATH_LEN];uint8_t file_data[MAX_FILEDATA_LEN];
};
? CRC32 實現(簡潔版本)
uint32_t crc32(const uint8_t* data, size_t len) {uint32_t crc = 0xFFFFFFFF;for (size_t i = 0; i < len; ++i) {crc ^= data[i];for (int j = 0; j < 8; ++j)crc = (crc >> 1) ^ (0xEDB88320 & -(crc & 1));}return ~crc;
}
? 發送端函數(PC)
bool send_file(int sock_fd, const char* src_path, const char* dst_path) {FILE* fp = fopen(src_path, "rb");if (!fp) {perror("open src file");return false;}fseek(fp, 0, SEEK_END);long file_size = ftell(fp);fseek(fp, 0, SEEK_SET);if (file_size <= 0 || file_size > MAX_FILEDATA_LEN) {fprintf(stderr, "Invalid file size: %ld\n", file_size);fclose(fp);return false;}FilePacket pkt = {};pkt.cmd_id = 0x01;pkt.file_size = file_size;fread(pkt.file_data, 1, file_size, fp);fclose(fp);pkt.file_crc32 = crc32(pkt.file_data, file_size);strncpy(pkt.file_name, basename((char*)src_path), MAX_FILENAME_LEN);strncpy(pkt.file_path, dst_path, MAX_FILEPATH_LEN);if (send(sock_fd, &pkt, sizeof(pkt), 0) != sizeof(pkt)) {perror("send failed");return false;}printf("Send file OK: %s (%ld bytes)\n", src_path, file_size);return true;
}
? 接收端函數(設備)
bool recv_file(int sock_fd) {FilePacket pkt;ssize_t n = recv(sock_fd, &pkt, sizeof(pkt), 0);if (n != sizeof(pkt)) {perror("recv failed or incomplete");return false;}uint32_t calc_crc = crc32(pkt.file_data, pkt.file_size);if (calc_crc != pkt.file_crc32) {fprintf(stderr, "CRC32 mismatch! recv=0x%08x, calc=0x%08x\n", pkt.file_crc32, calc_crc);return false;}FILE* fp = fopen(pkt.file_path, "wb");if (!fp) {perror("open write file");return false;}fwrite(pkt.file_data, 1, pkt.file_size, fp);fclose(fp);printf("File received: %s (%u bytes), saved to %s\n",pkt.file_name, pkt.file_size, pkt.file_path);// 自動解壓char cmd[512];snprintf(cmd, sizeof(cmd), "unzip -o %s -d /tmp/unpack_dir/", pkt.file_path);int ret = system(cmd);if (ret != 0) {fprintf(stderr, "unzip failed\n");return false;}printf("Unzip complete.\n");return true;
}
? 示例調用方式
PC 端發送:
send_file(sock_fd, "./update.zip", "/tmp/recv.zip");
設備端接收并解壓:
recv_file(sock_fd);
🧱 后續建議擴展
功能 | 可做法 |
---|---|
ACK/NACK | 接收端校驗成功后返回 ACK |
分包大文件 | 適配 chunk 傳輸結構體 |
解壓失敗回滾 | 解壓前備份舊文件 |
解壓路徑配置 | 結構體中加入 unpack_path |
是否需要我幫你打包成完整工程目錄(帶 Makefile/CMake)或對接到 FunctionFS 項目中?你也可以告訴我你當前的傳輸通道(USB FunctionFS / Socket / Pipe)我幫你定制集成。是否繼續?