//version 1.0.1 #include #include #include #include #include "esp_log.h" #include "MINIZ/miniz.h" #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_task_wdt.h" #include "event.h" #include static const char *TAG_ZIP = "ZIP_EXTRACTOR"; /* 最近一次解压成功对应的表情包 ID(如 'A'/'B'),供 main 识别下载成功并播放 */ static char s_last_downloaded_pack_id = '0'; char zip_extractor_get_last_downloaded_pack(void) { return s_last_downloaded_pack_id; } void extract_zip_file(const char* zip_path, const char* extract_dir); // static mz_bool extract_file_with_callback(mz_zip_archive* zip_archive, mz_uint file_index, const char* output_path); // static mz_bool extract_file_low_memory(mz_zip_archive* zip_archive, mz_uint file_index, const char* output_path); // static void check_memory_status(void); // void extract_zip_file_optimized(const char* zip_path, const char* extract_dir); const char* get_zip_error_string(mz_zip_error error); void extract_zip_file(const char* zip_path, const char* extract_dir); // 创建目录的函数 static int create_directory(const char *path) { char dir_path[256]; char *p; strncpy(dir_path, path, sizeof(dir_path) - 1); dir_path[sizeof(dir_path) - 1] = '\0'; // 逐级创建目录 for (p = dir_path + 1; *p; p++) { if (*p == '/') { *p = '\0'; mkdir(dir_path, 0755); // 创建目录,忽略错误(目录可能已存在) *p = '/'; } } printf("%s\n0",dir_path); return mkdir(dir_path, 0755); // 创建最终目录 } // 检查文件是否存在 static bool file_exists(const char *path) { struct stat st; return (stat(path, &st) == 0); } // // 从文件路径中提取目录部分 static void extract_directory(const char *filepath, char *dir_path, size_t dir_path_size) { const char *last_slash = strrchr(filepath, '/'); printf("%s\n",last_slash); if (last_slash) { size_t dir_len = last_slash - filepath; if (dir_len < dir_path_size - 1) { strncpy(dir_path, filepath, dir_len); printf("%d\n",dir_len); dir_path[dir_len] = '\0'; } else { dir_path[0] = '\0'; } } else { // 没有目录,只有文件名 dir_path[0] = '\0'; } } void extract_zip_file(const char* zip_path, const char* extract_dir) { ESP_LOGI(TAG_ZIP, "开始解压ZIP文件: %s 到目录: %s", zip_path, extract_dir); // 使用静态或堆分配的大缓冲区,避免栈分配 mz_zip_archive* zip_archive = (mz_zip_archive*)malloc(sizeof(mz_zip_archive)); mz_zip_zero_struct(zip_archive); // 使用 miniz 的专用初始化函数 if (zip_archive == NULL) { ESP_LOGE(TAG_ZIP, "内存不足"); return; } ESP_LOGI(TAG_ZIP, "Main task stack free: %d", uxTaskGetStackHighWaterMark(NULL)); ESP_LOGW(TAG_ZIP, "内存足够"); // 1. 初始化ZIP读取器 if (!mz_zip_reader_init_file(zip_archive, zip_path, 0)) { ESP_LOGE(TAG_ZIP, "无法初始化ZIP读取器: %s", zip_path); return; } ESP_LOGI(TAG_ZIP, "ZIP文件打开成功"); // 2. 获取文件数量 mz_uint num_files = mz_zip_reader_get_num_files(zip_archive); ESP_LOGI(TAG_ZIP, "ZIP文件中包含 %u 个文件", num_files); // 使用堆分配路径缓冲区,避免大数组在栈上 char *output_path = malloc(1024); char *dir_path = malloc(1024); if (!output_path || !dir_path) { ESP_LOGE(TAG_ZIP, "内存分配失败"); goto cleanup; } // 3. 遍历并解压每个文件 for (mz_uint i = 0; i < num_files; i++) { mz_zip_archive_file_stat file_stat; if (mz_zip_reader_file_stat(zip_archive, i, &file_stat)) { // 跳过目录条目 if (mz_zip_reader_is_file_a_directory(zip_archive, i)) { ESP_LOGI(TAG_ZIP, "跳过目录: %s", file_stat.m_filename); continue; } ESP_LOGI(TAG_ZIP, "处理文件: %s (大小: %llu 字节)", file_stat.m_filename, file_stat.m_uncomp_size); // 构建完整的输出文件路径 snprintf(output_path, 1024, "%s/%s", extract_dir, file_stat.m_filename); //char *underscore = strchr(output_path, '_'); //if (underscore) *underscore = '\0'; // 检查并创建必要的目录 extract_directory(output_path, dir_path, 1024); if (strlen(dir_path) > 0) { create_directory(dir_path); } char* last_slash = strrchr(output_path, '/'); if (last_slash != NULL) { // 斜杠后面的部分就是文件名 char* filename = last_slash + 1; printf("文件名: %s\n", filename); snprintf(output_path, 512, "%s/%s", extract_dir, filename); } else { printf("没有找到斜杠\n"); } FILE *f = fopen(output_path, "wb"); if (!f) { ESP_LOGE("FILE", "无法打开文件: %s, errno=%d (%s)", output_path, errno, strerror(errno)); return; } // printf("%s0\n",output_path); // FILE* f = fopen(output_path, "wb"); fclose(f); //taskENTER_CRITICAL(); /* FILE* f = fopen(output_path, "wb"); if (!f) { ESP_LOGE("FILE", "无法打开文件: %s", output_path); return; } // 验证文件指针 if (fseek(f, 0, SEEK_SET) != 0) { ESP_LOGE("FILE", "文件指针无效: %s", output_path); fclose(f); return; }*/ // ESP_LOGI("FILE", "文件打开成功: %p, 路径: %s", f, output_path); // void*ff = (void*)f; // ESP_LOGI("void*addr", "文件打开成功: %p, 路径: %s", ff, output_path); // FILE* fff = (FILE*)ff; // ESP_LOGI("FILE", "文件打开成功: %p, 路径: %s", fff, output_path); //fclose(f); mz_bool result = mz_zip_reader_extract_to_file(zip_archive, i, output_path, MZ_ZIP_FLAG_CASE_SENSITIVE | MZ_ZIP_FLAG_IGNORE_PATH); // 8. 检查解压结果 if (!result) { mz_zip_error error = mz_zip_get_last_error(zip_archive); ESP_LOGE(TAG_ZIP, "解压失败, 错误: %s (%d)", get_zip_error_string(error), error); // 根据错误类型提供具体建议 switch (error) { case MZ_ZIP_NO_ERROR: ESP_LOGI(TAG_ZIP, "操作成功完成"); break; case MZ_ZIP_FILE_OPEN_FAILED: ESP_LOGE(TAG_ZIP, "建议: 检查文件路径、权限或磁盘空间,确认文件存在"); break; case MZ_ZIP_FILE_CREATE_FAILED: ESP_LOGE(TAG_ZIP, "建议: 检查输出目录权限和磁盘空间"); break; case MZ_ZIP_FILE_WRITE_FAILED: ESP_LOGE(TAG_ZIP, "建议: 磁盘空间不足或文件系统只读"); break; case MZ_ZIP_FILE_READ_FAILED: ESP_LOGE(TAG_ZIP, "建议: 文件可能被占用或损坏"); break; case MZ_ZIP_DECOMPRESSION_FAILED: ESP_LOGE(TAG_ZIP, "建议: ZIP文件可能已损坏或使用不支持的压缩算法"); break; case MZ_ZIP_CRC_CHECK_FAILED: ESP_LOGE(TAG_ZIP, "建议: 文件校验失败,可能下载不完整或被修改"); break; case MZ_ZIP_UNSUPPORTED_METHOD: ESP_LOGE(TAG_ZIP, "建议: 使用不支持的压缩方法,尝试使用标准ZIP格式"); break; case MZ_ZIP_UNSUPPORTED_ENCRYPTION: ESP_LOGE(TAG_ZIP, "建议: 不支持加密的ZIP文件"); break; case MZ_ZIP_ALLOC_FAILED: ESP_LOGE(TAG_ZIP, "建议: 系统内存不足,关闭其他应用或减小文件大小"); break; case MZ_ZIP_BUF_TOO_SMALL: ESP_LOGE(TAG_ZIP, "建议: 缓冲区太小,增加缓冲区大小"); break; case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED: ESP_LOGE(TAG_ZIP, "建议: ZIP文件头损坏或不是有效的ZIP文件"); break; case MZ_ZIP_NOT_AN_ARCHIVE: ESP_LOGE(TAG_ZIP, "建议: 文件不是有效的ZIP归档文件"); break; case MZ_ZIP_FILE_TOO_LARGE: ESP_LOGE(TAG_ZIP, "建议: 文件太大,超出系统限制"); break; case MZ_ZIP_ARCHIVE_TOO_LARGE: ESP_LOGE(TAG_ZIP, "建议: 归档文件总体积太大"); break; case MZ_ZIP_TOO_MANY_FILES: ESP_LOGE(TAG_ZIP, "建议: 归档内文件数量过多"); break; case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR: ESP_LOGE(TAG_ZIP, "建议: 无法找到中央目录,ZIP文件不完整"); break; case MZ_ZIP_UNSUPPORTED_MULTIDISK: ESP_LOGE(TAG_ZIP, "建议: 不支持多卷ZIP文件"); break; case MZ_ZIP_WRITE_CALLBACK_FAILED: ESP_LOGE(TAG_ZIP, "建议: 写回调函数失败,检查文件系统状态"); break; case MZ_ZIP_FILE_SEEK_FAILED: ESP_LOGE(TAG_ZIP, "建议: 文件定位失败,可能文件被截断"); break; case MZ_ZIP_INVALID_FILENAME: ESP_LOGE(TAG_ZIP, "建议: 文件名包含非法字符或路径过长"); break; case MZ_ZIP_INVALID_PARAMETER: ESP_LOGE(TAG_ZIP, "建议: 函数参数无效,检查调用代码"); break; case MZ_ZIP_FILE_NOT_FOUND: ESP_LOGE(TAG_ZIP, "建议: 指定的文件不存在"); break; case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE: ESP_LOGE(TAG_ZIP, "建议: 解压后大小与预期不符,文件可能损坏"); break; case MZ_ZIP_COMPRESSION_FAILED: ESP_LOGE(TAG_ZIP, "建议: 压缩过程失败"); break; case MZ_ZIP_FILE_CLOSE_FAILED: ESP_LOGE(TAG_ZIP, "建议: 文件关闭失败,可能磁盘错误"); break; case MZ_ZIP_FILE_STAT_FAILED: ESP_LOGE(TAG_ZIP, "建议: 无法获取文件状态信息"); break; case MZ_ZIP_VALIDATION_FAILED: ESP_LOGE(TAG_ZIP, "建议: ZIP文件验证失败"); break; case MZ_ZIP_UNDEFINED_ERROR: case MZ_ZIP_INTERNAL_ERROR: case MZ_ZIP_TOTAL_ERRORS: default: ESP_LOGE(TAG_ZIP, "建议: 检查系统资源、文件完整性和库版本"); break; } } // 直接解压到文件 // if (mz_zip_reader_extract_to_file(&zip_archive, i, output_path, 0)) { // ESP_LOGI(TAG, "成功解压到: %s", output_path); // } else { ESP_LOGI(TAG_ZIP, "解压成功: %s", output_path); } // 让出CPU,避免长时间占用 if (i % 5 == 0) { vTaskDelay(pdMS_TO_TICKS(1)); } } } cleanup: free(output_path); free(dir_path); // 4. 结束读取 if (!mz_zip_reader_end(zip_archive)) { ESP_LOGE(TAG_ZIP, "[EMOJI_DEBUG] ZIP读取器结束失败"); set_system_event(DOWNLOAD_FAILED_BIT); ESP_LOGE(TAG_ZIP, "[EMOJI_DEBUG] 已设置 DOWNLOAD_FAILED_BIT"); } else { ESP_LOGI(TAG_ZIP, "[EMOJI_DEBUG] ZIP解压完成, extract_dir=%s", extract_dir); const char *last_slash = strrchr(extract_dir, '/'); ESP_LOGI(TAG_ZIP, "[EMOJI_DEBUG] strrchr(extract_dir, '/') = %s", last_slash ? last_slash : "NULL"); if (last_slash && *(last_slash + 1)) { s_last_downloaded_pack_id = *(last_slash + 1); ESP_LOGI(TAG_ZIP, "[EMOJI_DEBUG] 从目录名解析 pack_id='%c'", s_last_downloaded_pack_id); } else { s_last_downloaded_pack_id = '0'; ESP_LOGW(TAG_ZIP, "[EMOJI_DEBUG] 无法从目录名解析 pack_id, 设为 '0'"); } /* 列出解压目录下的文件,确认解压结果 */ DIR *dir = opendir(extract_dir); if (dir) { struct dirent *ent; int file_count = 0; ESP_LOGI(TAG_ZIP, "[EMOJI_DEBUG] 解压目录 %s 文件列表:", extract_dir); while ((ent = readdir(dir)) != NULL) { if (ent->d_name[0] == '.') continue; ESP_LOGI(TAG_ZIP, "[EMOJI_DEBUG] - %s", ent->d_name); file_count++; } closedir(dir); ESP_LOGI(TAG_ZIP, "[EMOJI_DEBUG] 解压目录共 %d 个文件", file_count); char default_check[128]; snprintf(default_check, sizeof(default_check), "%s/default.json", extract_dir); struct stat st; if (stat(default_check, &st) == 0) { ESP_LOGI(TAG_ZIP, "[EMOJI_DEBUG] default.json 存在, 大小=%ld bytes", (long)st.st_size); } else { ESP_LOGW(TAG_ZIP, "[EMOJI_DEBUG] default.json 不存在于 %s !", extract_dir); } } else { ESP_LOGE(TAG_ZIP, "[EMOJI_DEBUG] 无法打开解压目录 %s", extract_dir); } ESP_LOGI(TAG_ZIP, "[EMOJI_DEBUG] 准备设置 DOWNLOAD_SUCCESS_BIT, pack_id='%c'", s_last_downloaded_pack_id); set_system_event(DOWNLOAD_SUCCESS_BIT); ESP_LOGI(TAG_ZIP, "[EMOJI_DEBUG] DOWNLOAD_SUCCESS_BIT 已设置"); } free(zip_archive); } const char* get_zip_error_string(mz_zip_error error) { switch (error) { case MZ_ZIP_NO_ERROR: return "MZ_ZIP_NO_ERROR"; case MZ_ZIP_UNDEFINED_ERROR: return "MZ_ZIP_UNDEFINED_ERROR"; case MZ_ZIP_TOO_MANY_FILES: return "MZ_ZIP_TOO_MANY_FILES"; case MZ_ZIP_FILE_TOO_LARGE: return "MZ_ZIP_FILE_TOO_LARGE"; case MZ_ZIP_UNSUPPORTED_METHOD: return "MZ_ZIP_UNSUPPORTED_METHOD"; case MZ_ZIP_UNSUPPORTED_ENCRYPTION: return "MZ_ZIP_UNSUPPORTED_ENCRYPTION"; case MZ_ZIP_UNSUPPORTED_FEATURE: return "MZ_ZIP_UNSUPPORTED_FEATURE"; case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR: return "MZ_ZIP_FAILED_FINDING_CENTRAL_DIR"; case MZ_ZIP_NOT_AN_ARCHIVE: return "MZ_ZIP_NOT_AN_ARCHIVE"; case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED: return "MZ_ZIP_INVALID_HEADER_OR_CORRUPTED"; case MZ_ZIP_UNSUPPORTED_MULTIDISK: return "MZ_ZIP_UNSUPPORTED_MULTIDISK"; case MZ_ZIP_DECOMPRESSION_FAILED: return "MZ_ZIP_DECOMPRESSION_FAILED"; case MZ_ZIP_COMPRESSION_FAILED: return "MZ_ZIP_COMPRESSION_FAILED"; case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE: return "MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE"; case MZ_ZIP_CRC_CHECK_FAILED: return "MZ_ZIP_CRC_CHECK_FAILED"; case MZ_ZIP_UNSUPPORTED_CDIR_SIZE: return "MZ_ZIP_UNSUPPORTED_CDIR_SIZE"; case MZ_ZIP_ALLOC_FAILED: return "MZ_ZIP_ALLOC_FAILED"; case MZ_ZIP_FILE_OPEN_FAILED: return "MZ_ZIP_FILE_OPEN_FAILED"; case MZ_ZIP_FILE_CREATE_FAILED: return "MZ_ZIP_FILE_CREATE_FAILED"; case MZ_ZIP_FILE_WRITE_FAILED: return "MZ_ZIP_FILE_WRITE_FAILED"; case MZ_ZIP_FILE_READ_FAILED: return "MZ_ZIP_FILE_READ_FAILED"; case MZ_ZIP_FILE_CLOSE_FAILED: return "MZ_ZIP_FILE_CLOSE_FAILED"; case MZ_ZIP_FILE_SEEK_FAILED: return "MZ_ZIP_FILE_SEEK_FAILED"; case MZ_ZIP_FILE_STAT_FAILED: return "MZ_ZIP_FILE_STAT_FAILED"; case MZ_ZIP_INVALID_PARAMETER: return "MZ_ZIP_INVALID_PARAMETER"; case MZ_ZIP_INVALID_FILENAME: return "MZ_ZIP_INVALID_FILENAME"; case MZ_ZIP_BUF_TOO_SMALL: return "MZ_ZIP_BUF_TOO_SMALL"; case MZ_ZIP_INTERNAL_ERROR: return "MZ_ZIP_INTERNAL_ERROR"; case MZ_ZIP_FILE_NOT_FOUND: return "MZ_ZIP_FILE_NOT_FOUND"; case MZ_ZIP_ARCHIVE_TOO_LARGE: return "MZ_ZIP_ARCHIVE_TOO_LARGE"; case MZ_ZIP_VALIDATION_FAILED: return "MZ_ZIP_VALIDATION_FAILED"; case MZ_ZIP_WRITE_CALLBACK_FAILED: return "MZ_ZIP_WRITE_CALLBACK_FAILED"; default: return "UNKNOWN_ERROR"; } } void extract_json_zip(char* filename) { ESP_LOGI(TAG_ZIP, "[EMOJI_DEBUG] ===== extract_json_zip 开始 ====="); ESP_LOGI(TAG_ZIP, "[EMOJI_DEBUG] filename=%s", filename ? filename : "NULL"); if (!filename || filename[0] == '\0') { ESP_LOGE(TAG_ZIP, "[EMOJI_DEBUG] extract_json_zip: filename 为空"); return; } char zip_file_path[64] = {0}; char extract_directory[96] = {0}; const char *base = strrchr(filename, '/'); if (base) base++; else base = filename; ESP_LOGI(TAG_ZIP, "[EMOJI_DEBUG] base (去除路径后)=%s", base); snprintf(zip_file_path, sizeof(zip_file_path), "%s/%s", "/extflash", filename); char pack_name[16] = {0}; size_t blen = strlen(base); if (blen > 4 && strcasecmp(base + blen - 4, ".zip") == 0) { strncpy(pack_name, base, blen - 4); pack_name[blen - 4] = '\0'; } else { strncpy(pack_name, base, sizeof(pack_name) - 1); pack_name[sizeof(pack_name) - 1] = '\0'; } snprintf(extract_directory, sizeof(extract_directory), "%s/%s", "/extflash", pack_name); ESP_LOGI(TAG_ZIP, "[EMOJI_DEBUG] zip_file_path=%s", zip_file_path); ESP_LOGI(TAG_ZIP, "[EMOJI_DEBUG] pack_name=%s", pack_name); ESP_LOGI(TAG_ZIP, "[EMOJI_DEBUG] extract_directory=%s", extract_directory); struct stat st; if (stat(zip_file_path, &st) != 0) { ESP_LOGE(TAG_ZIP, "[EMOJI_DEBUG] ZIP文件不存在: %s", zip_file_path); set_system_event(DOWNLOAD_FAILED_BIT); return; } ESP_LOGI(TAG_ZIP, "[EMOJI_DEBUG] 找到ZIP文件, 大小=%ld bytes", (long)st.st_size); int mk_ret = mkdir(extract_directory, 0755); ESP_LOGI(TAG_ZIP, "[EMOJI_DEBUG] mkdir(%s) 返回: %d", extract_directory, mk_ret); extract_zip_file(zip_file_path, extract_directory); ESP_LOGI(TAG_ZIP, "[EMOJI_DEBUG] ===== extract_json_zip 结束 ====="); }