鸿蒙-ArkTS和Native之间的交互使用8使用libarchive创建compress的napi

仿佛云烟
发布于 2025-6-28 16:49
浏览
0收藏

前言:


        Libarchive 是一个开源的多格式存档和压缩库,支持多种常见存档格式的读写操作,广泛应用于跨平台数据处理、软件打包工具、文件管理等领域。

  1. 多格式兼容性
    libarchive 支持超过 20 种存档与压缩格式,包括:
  • 经典格式:tar(含 gzip/bzip2/xz 压缩);
  • 压缩包格式:ZIP、7-Zip、RAR(仅解压);
  • 系统专用格式:Apple Disk Image (DMG)、Microsoft Cabinet (CAB);
  • 冷门格式:Shar、WARC 等。
    其格式支持广度显著优于同类库(如 zlib 或 libzip),尤其适合需处理异构存档文件的场景。
  1. 跨平台与许可证
    基于 BSD 许可证开源,可无缝集成于商业项目。支持 Linux、Windows、macOS 等主流操作系统,并提供 C API,便于嵌入到 Python、Node.js 等语言的扩展中。

压缩compress:

在鸿蒙的cpp代码中添加如下:

//Index.d.ts文件中添加
export const compress: (inFile: string, outFile: string, format: string) => object


然后:添加c代码

//在napi_init.cpp文件中

static napi_value Compress(napi_env env, napi_callback_info info) {
    size_t argc = 3;
    napi_value argv[3] = {nullptr};
    napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);

    size_t in_file_size;
    char in_file_buf[256];
    napi_get_value_string_utf8(env, argv[0], in_file_buf, sizeof(in_file_buf), &in_file_size);
    std::string in_file(in_file_buf, in_file_size);

    size_t out_file_size;
    char out_file_buf[256];
    napi_get_value_string_utf8(env, argv[1], out_file_buf, sizeof(out_file_buf), &out_file_size);
    std::string out_file(out_file_buf, out_file_size);

    size_t format_size;
    char format_buf[10];
    napi_get_value_string_utf8(env, argv[2], format_buf, sizeof(format_buf), &format_size);
    std::string format(format_buf, format_size);

    std::string dir_path = get_dir(out_file);
    std::string temp_file_path = dir_path.empty() ? "/tmp/temp_archive_XXXXXX" : dir_path + "/temp_archive_XXXXXX";

    int temp_fd = mkstemp(&temp_file_path[0]);
    if (temp_fd == -1) {
        return create_result(env, ErrorCode::CREATE_TEMP_FILE_ERROR);
    }

    struct archive *a;
    struct archive *out;
    a = archive_read_disk_new();
    archive_read_disk_set_standard_lookup(a);

    out = archive_write_new();
    if (format == "7z") {
        archive_write_set_format_7zip(out);
    } else if (format == "tar") {
        archive_write_set_format_pax_restricted(out);
    } else if (format == "zip") {
        archive_write_set_format_zip(out);
    } else if (format == "gz") {
        archive_write_set_format_gnutar(out);
        archive_write_add_filter_gzip(out);
    } else if (format == "xz") {
        archive_write_set_format_gnutar(out);
        archive_write_add_filter_xz(out);
    } else {
        return create_result(env, ErrorCode::UNSUPPORTED_FORMAT);
    }

    if (archive_write_open_fd(out, temp_fd) != ARCHIVE_OK) {
        close(temp_fd);
        return create_result(env, ErrorCode::ARCHIVE_ERROR, archive_error_string(out));
    }
    
    const char *original_locale = setlocale(LC_CTYPE, nullptr);
    setlocale(LC_CTYPE, "en_US.UTF-8");

    if (is_directory(in_file)) {
        if (in_file.back() == '/') {
            compress_directory(a, out, in_file.substr(0, in_file.size() - 1), "");
        } else {
            compress_directory(a, out, in_file, get_name(in_file));
        }
    } else {
        compress_file(out, in_file, get_name(in_file));
    }

    archive_write_close(out);
    archive_write_free(out);
    archive_read_free(a);
    close(temp_fd);
    
    setlocale(LC_CTYPE, original_locale);

    if (rename(temp_file_path.c_str(), out_file.c_str()) != 0) {
        return create_result(env, ErrorCode::RENAME_FILE_ERROR);
    }

    return create_result(env, ErrorCode::SUCCESS);
}

创建返回值:

static napi_value create_result(napi_env env, ErrorCode code, const char *custom_message = nullptr) {
    napi_value result;
    napi_create_object(env, &result);
    
    napi_value error_code;
    napi_create_int32(env, static_cast<int32_t>(code), &error_code);
    napi_set_named_property(env, result, "code", error_code);

    const char *message = error_code_to_message(code, custom_message);
    napi_value error_message;
    napi_create_string_utf8(env, message, NAPI_AUTO_LENGTH, &error_message);
    napi_set_named_property(env, result, "message", error_message);
    return result;
}

解析文件方法:

void compress_file(struct archive *out, const std::string &file_path, const std::string &base_file_path) {
    struct stat st;
    if (stat(file_path.c_str(), &st) != 0) {
        OH_LOG_ERROR(LOG_APP, "Unable to stat file: %{public}s", file_path.c_str());
        return;
    }

    struct archive_entry *entry = archive_entry_new();
    archive_entry_set_pathname(entry, base_file_path.c_str());
    archive_entry_set_size(entry, st.st_size);
    archive_entry_set_filetype(entry, AE_IFREG);
    archive_entry_set_perm(entry, st.st_mode & 0777);
    archive_write_header(out, entry);

    int fd = open(file_path.c_str(), O_RDONLY);
    if (fd == -1) {
        OH_LOG_ERROR(LOG_APP, "Unable to open file: %{public}s", file_path.c_str());
        archive_entry_free(entry);
        return;
    }

    char buffer[1024 * 1024];
    ssize_t bytes_read;
    while ((bytes_read = read(fd, buffer, sizeof(buffer))) > 0) {
        archive_write_data(out, buffer, bytes_read);
    }
    close(fd);
    archive_entry_free(entry);
}

解析文件夹方法:

void compress_directory(struct archive *a, struct archive *out, const std::string &dir_path, const std::string &base_dir) {
    DIR *dir = opendir(dir_path.c_str());
    if (dir == nullptr) {
        OH_LOG_ERROR(LOG_APP, "Unable to open directory: %{public}s", dir_path.c_str());
        return;
    }

    struct dirent *entry;
    bool is_empty = true;
    while ((entry = readdir(dir)) != nullptr) {
        if (entry->d_type == DT_DIR && strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
            is_empty = false;
            std::string sub_dir_path = dir_path + "/" + entry->d_name;
            std::string sub_base_dir = base_dir.empty() ? entry->d_name : base_dir + "/" + entry->d_name;
            compress_directory(a, out, sub_dir_path, sub_base_dir);
        } else if (entry->d_type == DT_REG) {
            is_empty = false;
            std::string file_path = dir_path + "/" + entry->d_name;
            std::string base_file_path = base_dir.empty() ? entry->d_name : base_dir + "/" + entry->d_name;
            compress_file(out, file_path, base_file_path);
        }
    }
    closedir(dir);

    if (is_empty) {
        struct archive_entry *entry = archive_entry_new();
        archive_entry_set_pathname(entry, base_dir.empty() ? get_name(dir_path).c_str() : base_dir.c_str());
        archive_entry_set_filetype(entry, AE_IFDIR);
        archive_entry_set_perm(entry, 0755);
        archive_write_header(out, entry);
        archive_entry_free(entry);
    }
}

分类
已于2025-6-28 16:50:47修改
收藏
回复
举报
回复
    相关推荐