使用pako和base64实现文本压缩、解压,保证压缩后输出内容依旧是文本

import { deflate, inflate } from "pako";
import {
  readFileSync,
  writeFileSync,
  existsSync,
  mkdirSync,
  readdirSync,
  statSync,
} from "fs";
import { join, dirname, basename, extname } from "path";
 
// 判断操作类型和路径
const operation = process.argv[2]; // c 或 d
const inputPath = process.argv[3];
 
if (!operation || !inputPath) {
  console.error("Usage: node index.js [c|d] <folder_path>");
  process.exit(1);
}
 
// 处理压缩操作
if (operation === "c") {
  compressFolder(inputPath);
}
// 处理解压操作
else if (operation === "d") {
  decompressFolder(inputPath);
} else {
  console.error(
    "Invalid operation. Use 'c' for compress or 'd' for decompress"
  );
  process.exit(1);
}
 
// 压缩文件夹
function compressFolder(folderPath) {
  if (!existsSync(folderPath)) {
    console.error(`Folder ${folderPath} does not exist`);
    process.exit(1);
  }
 
  const outputFolder = folderPath + "_compressed";
  if (!existsSync(outputFolder)) {
    mkdirSync(outputFolder, { recursive: true });
  }
 
  processFolder(folderPath, outputFolder, (filePath, outputPath) => {
    try {
      // 读取文件内容
      const content = readFileSync(filePath, "utf-8");
 
      // 压缩内容
      const compressed = deflate(content, { to: "string" });
 
      // 转换为base64字符串
      const compressedString = Buffer.from(compressed).toString("base64");
 
      // 写入压缩文件
      const compressedFilePath = outputPath + ".compressed";
      writeFileSync(compressedFilePath, compressedString, "utf-8");
 
      console.log(`Compressed: ${filePath} -> ${compressedFilePath}`);
    } catch (error) {
      console.error(`Error compressing ${filePath}:`, error.message);
    }
  });
}
 
// 解压文件夹
function decompressFolder(folderPath) {
  if (!existsSync(folderPath)) {
    console.error(`Folder ${folderPath} does not exist`);
    process.exit(1);
  }
 
  // 修改输出目录逻辑:在输入目录的同级创建解压目录
  const baseName = basename(folderPath);
  const dirName = dirname(folderPath);
  const outputFolder = join(
    dirName,
    baseName.replace(/_compressed$/, "") + "_decompressed"
  );
 
  if (!existsSync(outputFolder)) {
    mkdirSync(outputFolder, { recursive: true });
  }
 
  processFolder(folderPath, outputFolder, (filePath, outputPath) => {
    // 只处理压缩文件
    if (filePath.endsWith(".compressed")) {
      try {
        // 读取压缩文件内容
        const compressedData = readFileSync(filePath, "utf-8");
 
        // 解码base64
        const compressedBuffer = Buffer.from(compressedData, "base64");
 
        // 解压内容
        const decompressed = inflate(compressedBuffer, { to: "string" });
 
        // 移除 .compressed 扩展名
        const decompressedFilePath = outputPath.replace(/\.compressed$/, "");
        writeFileSync(decompressedFilePath, decompressed, "utf-8");
 
        console.log(`Decompressed: ${filePath} -> ${decompressedFilePath}`);
      } catch (error) {
        console.error(`Error decompressing ${filePath}:`, error.message);
      }
    }
  });
}
 
// 遍历文件夹并处理文件
function processFolder(inputFolder, outputFolder, processFileFn) {
  function walkDir(currentInputPath, currentOutputPath) {
    const items = readdirSync(currentInputPath);
 
    for (const item of items) {
      const inputItemPath = join(currentInputPath, item);
      const outputItemPath = join(currentOutputPath, item);
 
      const stats = statSync(inputItemPath);
 
      if (stats.isDirectory()) {
        // 创建对应的输出目录
        const newOutputPath = outputItemPath;
        if (!existsSync(newOutputPath)) {
          mkdirSync(newOutputPath, { recursive: true });
        }
        // 递归处理子目录
        walkDir(inputItemPath, newOutputPath);
      } else if (stats.isFile()) {
        // 处理文件
        processFileFn(inputItemPath, outputItemPath);
      }
    }
  }
 
  walkDir(inputFolder, outputFolder);
}

使用方法:

  1. 安装依赖:npm install pako
  2. 压缩:node index.js c <folder_path>
  3. 解压:node index.js d <folder_path>