markdownzip
v0.1.1
Published
A powerful tool to package and manage Markdown documents with their resources in a single file
Downloads
8
Maintainers
Readme
MarkdownZip (MDZ)
MarkdownZip (MDZ) 是一个专为Markdown文档设计的打包格式,它能将Markdown文件及其相关资源(图片、视频、附件等)封装为单一的自包含文件,实现文档的便捷分享与存储。MDZ格式支持密码保护、完整性校验等高级特性。
特性
- 将Markdown内容和所有资源文件封装到单一文件中
- 支持密码加密保护敏感内容
- 文件完整性校验,防止文件损坏
- 安全的文件操作,防止路径遍历攻击
- 支持丰富的元数据(标题、作者、创建和修改时间等)
- 简单易用的TypeScript API
- 支持命令行操作
安装
NPM包安装
npm install markdownzip全局安装CLI工具
npm install -g markdownzipCLI使用说明
MDZ提供了命令行工具,方便快速创建、查看和管理MDZ文件。
基本命令
# 创建MDZ文件
mdz create <output.mdz> <input.md> [选项]
# 提取MDZ文件内容
mdz extract <input.mdz> <output.md> [选项]
# 显示MDZ文件信息
mdz info <input.mdz> [选项]
# 添加资源到MDZ文件
mdz add <mdz文件> <资源文件> [选项]
# 列出MDZ文件中的资源
mdz list <mdz文件> [选项]
# 更新MDZ文件的元数据
mdz metadata <mdz文件> [选项]
# 设置或更改MDZ文件的密码
mdz password <mdz文件> [选项]
# 验证MDZ文件的完整性
mdz verify <mdz文件> [选项]
# 提取MDZ文件中的特定资源
mdz extract-asset <mdz文件> <资源ID> <输出路径> [选项]
# 显示版本信息
mdz version
# 显示帮助信息
mdz help
mdz --help常用选项
全局选项:
--password=<密码>或-p <密码>- 设置或提供密码--verbose或-v- 显示详细信息--quiet或-q- 减少输出信息--force或-f- 强制操作,忽略警告
create 命令特有选项:
--title=<标题>或-t <标题>- 设置文档标题--author=<作者>或-a <作者>- 设置文档作者
extract 命令特有选项:
--overwrite- 覆盖已存在的文件
add 命令特有选项:
--relative-path=<路径>或-r <路径>- 设置资源在MDZ中的相对路径
list 命令特有选项:
--json- 以JSON格式输出资源列表
metadata 命令特有选项:
--title=<标题>或-t <标题>- 更新文档标题--author=<作者>或-a <作者>- 更新文档作者--json=<json>- 以JSON格式提供完整的元数据更新
password 命令特有选项:
--new-password=<密码>- 设置新密码--remove- 移除密码保护
命令详解
create - 创建MDZ文件
从Markdown文件创建新的MDZ文件。
mdz create example.mdz README.md --title="示例文档" --author="作者" --password=123456此命令会从README.md创建一个新的MDZ文件,设置标题为"示例文档",作者为"作者",并添加密码保护。输出文件为example.mdz。
extract - 提取MDZ文件内容
将MDZ文件的主要内容提取到Markdown文件中。
mdz extract example.mdz extracted.md --password=123456此命令会从example.mdz提取主要内容到extracted.md文件中。如果文件有密码保护,需要提供密码。
info - 显示MDZ文件信息
显示MDZ文件的元数据和资源列表。
mdz info example.mdz --password=123456输出包括文件标题、作者、版本、创建和更新时间、是否加密以及资源列表等信息。
add - 添加资源到MDZ文件
向MDZ文件添加一个资源文件。
mdz add example.mdz image.png --password=123456此命令会向example.mdz文件添加image.png资源,并返回资源ID。
list - 列出MDZ文件中的资源
列出MDZ文件中所有资源的详细信息。
mdz list example.mdz --password=123456
mdz list example.mdz --json # 以JSON格式输出metadata - 更新MDZ文件的元数据
更新MDZ文件的元数据信息,如标题、作者等。
mdz metadata example.mdz --title="新标题" --author="新作者" --password=123456
mdz metadata example.mdz --json='{"title":"JSON格式标题","author":"JSON格式作者"}'password - 设置或更改MDZ文件的密码
设置新密码、更改现有密码或移除密码保护。
# 设置新密码或更改现有密码
mdz password example.mdz --password=123456 --new-password=654321
# 移除密码保护
mdz password example.mdz --password=123456 --removeverify - 验证MDZ文件的完整性
验证MDZ文件中所有资源的校验和,检查文件是否被篡改或损坏。
mdz verify example.mdz --password=123456extract-asset - 提取MDZ文件中的特定资源
根据资源ID提取MDZ文件中的特定资源到指定路径。
mdz extract-asset example.mdz 5f2d8da6-e716-4c0e-8b0c-ec92d0b44dbd ./output.png --password=123456示例场景
基本工作流程
# 1. 创建MDZ文件
mdz create document.mdz README.md --title="项目文档" --author="团队"
# 2. 查看MDZ文件信息
mdz info document.mdz
# 3. 添加资源
mdz add document.mdz logo.png
mdz add document.mdz screenshot.jpg
# 4. 列出所有资源
mdz list document.mdz
# 5. 提取内容到新文件
mdz extract document.mdz extracted.md使用密码保护
# 创建带密码的MDZ文件
mdz create secure.mdz confidential.md --password=123456 --title="机密文档"
# 提取受保护的MDZ文件
mdz extract secure.mdz extracted.md --password=123456
# 查看受保护的MDZ文件信息
mdz info secure.mdz --password=123456
# 验证文件完整性
mdz verify secure.mdz --password=123456
# 更改密码
mdz password secure.mdz --password=123456 --new-password=newpass元数据管理
# 创建MDZ文件
mdz create document.mdz README.md
# 更新元数据
mdz metadata document.mdz --title="更新的标题" --author="新作者"
# 使用JSON格式更新更多元数据字段
mdz metadata document.mdz --json='{"title":"JSON标题","author":"JSON作者","keywords":["markdown","mdz","文档"]}'批量处理
# 创建一个批处理脚本示例(bash)
#!/bin/bash
# 批量创建MDZ文件
for mdfile in *.md; do
mdz create "${mdfile%.md}.mdz" "$mdfile" --title="$(basename "${mdfile%.md}")" --author="自动化脚本"
done
# 批量验证MDZ文件
for mdzfile in *.mdz; do
mdz verify "$mdzfile"
doneAPI使用示例
创建一个新的MDZ文件
import { MDZFile } from 'markdownzip';
async function createMDZ() {
// 创建一个新的MDZ文件
const mdz = new MDZFile('example.mdz');
// 准备Markdown内容
const content = `# Hello MDZ
这是一个MDZ文档示例。

`;
// 创建文件
await mdz.create(content, {
title: '示例文档',
author: '张三',
password: '可选密码' // 可选
});
// 添加资源文件
await mdz.addAsset('/path/to/image.png');
// 保存文件
await mdz.save();
// 关闭文件
await mdz.close();
}打开并读取MDZ文件
import { MDZFile } from 'markdownzip';
async function openMDZ() {
// 创建MDZ实例
const mdz = new MDZFile('example.mdz');
// 打开文件(如果有密码保护,则提供密码)
await mdz.open('可选密码');
// 读取主要内容
const content = await mdz.getMainContent();
console.log(content);
// 获取元数据
const metadata = mdz.getMetadata();
console.log(`标题: ${metadata.title}`);
console.log(`作者: ${metadata.author}`);
// 获取资源列表
const assets = mdz.getAssets();
console.log(`资源数量: ${assets.length}`);
// 导出资源
if (assets.length > 0) {
await mdz.extractAsset(assets[0].id, '/path/to/output.png');
}
// 关闭文件
await mdz.close();
}编辑MDZ文件
import { MDZFile } from 'markdownzip';
async function editMDZ() {
// 打开现有文件
const mdz = new MDZFile('example.mdz');
await mdz.open('可选密码');
// 获取当前内容并修改
let content = await mdz.getMainContent();
content += '\n\n## 新章节\n\n这是一个新添加的章节。\n';
// 更新内容
await mdz.setMainContent(content);
// 更新元数据
mdz.updateMetadata({
title: '更新后的文档标题'
});
// 添加新资源
await mdz.addAsset('/path/to/another-image.jpg');
// 保存更改
await mdz.save();
// 关闭文件
await mdz.close();
}MDZ文件格式
MDZ文件本质上是一个ZIP文件,具有以下内部结构:
manifest.json- 包含元数据和资源列表的清单文件content/- 包含主Markdown文件及其他内容文件的目录main.md- 主Markdown文件
assets/- 包含所有资源文件的目录signature- 用于验证MDZ文件格式有效性的签名文件
API参考
核心类
MDZFile
主要的类,用于创建、读取和修改MDZ文件。
构造函数:
constructor(filePath?: string, options?: MDZOptions)方法:
| 方法 | 说明 | 参数 | 返回值 |
|------|------|------|--------|
| create | 创建新的MDZ文件 | mainContent: string, options?: MDZOptions | Promise<void> |
| open | 打开现有MDZ文件 | password?: string | Promise<void> |
| save | 保存MDZ文件 | targetPath?: string | Promise<void> |
| close | 关闭文件并清理临时资源 | - | Promise<void> |
| getMainContent | 获取主Markdown内容 | - | Promise<string> |
| setMainContent | 设置主Markdown内容 | content: string | Promise<void> |
| addAsset | 添加资源 | sourcePath: string, relativePath?: string | Promise<string> |
| removeAsset | 移除资源 | assetId: string | Promise<void> |
| getAssetContent | 获取资源内容 | assetId: string | Promise<Buffer> |
| extractAsset | 导出资源 | assetId: string, targetPath: string | Promise<void> |
| getAssets | 获取所有资源信息 | - | MDZAsset[] |
| getMetadata | 获取元数据 | - | MDZMetadata |
| updateMetadata | 更新元数据 | metadata: Partial<MDZMetadata> | void |
| setPassword | 设置或移除密码保护 | newPassword: string | null, currentPassword?: string | Promise<void> |
| isModified | 检查文件是否已修改 | - | boolean |
类型定义
MDZMetadata
MDZ文件的元数据接口。
| 属性 | 类型 | 必填 | 说明 |
|------|------|------|------|
| version | string | 是 | MDZ格式版本 |
| title | string | 否 | 文档标题 |
| author | string | 否 | 作者 |
| createdAt | string | 是 | 创建时间,ISO格式 |
| updatedAt | string | 是 | 更新时间,ISO格式 |
| encrypted | boolean | 是 | 是否加密 |
| checksum | string | 是 | 校验和 |
MDZAsset
MDZ文件的资源项目接口。
| 属性 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | string | 是 | 资源唯一标识符 |
| path | string | 是 | 资源在zip中的路径 |
| originalName | string | 是 | 原始文件名 |
| mimeType | string | 是 | MIME类型 |
| size | number | 是 | 文件大小(字节) |
| checksum | string | 是 | 文件校验和 |
MDZManifest
MDZ文件的清单接口。
| 属性 | 类型 | 必填 | 说明 |
|------|------|------|------|
| metadata | MDZMetadata | 是 | 元数据 |
| mainDocument | string | 是 | 主Markdown文件路径 |
| assets | MDZAsset[] | 是 | 资源列表 |
MDZOptions
创建或修改MDZ文件时的选项。
| 属性 | 类型 | 必填 | 说明 |
|------|------|------|------|
| title | string | 否 | 文档标题 |
| author | string | 否 | 作者 |
| password | string | 否 | 加密密码 |
| tempDir | string | 否 | 临时目录路径 |
| mainFileName | string | 否 | 主文件名(默认为main.md) |
MDZErrorType
MDZ操作错误类型枚举。
| 枚举值 | 说明 |
|------|------|
| FILE_NOT_FOUND | 文件未找到或无法访问 |
| INVALID_FORMAT | 无效的MDZ格式或结构 |
| ENCRYPTION_ERROR | 加密或解密错误 |
| CHECKSUM_MISMATCH | 校验和验证失败 |
| IO_ERROR | 输入/输出操作错误 |
| UNKNOWN_ERROR | 未知或未指定的错误 |
| PERMISSION_DENIED | 权限被拒绝 |
| VALIDATION_ERROR | 内容验证错误 |
| TIMEOUT | 操作超时 |
高级用法
处理错误
MDZ库使用自定义错误类型处理错误情况:
import { MDZFile, MDZError, MDZErrorType } from 'markdownzip';
async function handleMDZErrors() {
try {
const mdz = new MDZFile('example.mdz');
await mdz.open('错误密码');
} catch (error) {
if (error instanceof MDZError) {
switch (error.type) {
case MDZErrorType.ENCRYPTION_ERROR:
console.error('密码错误或加密问题:', error.message);
break;
case MDZErrorType.FILE_NOT_FOUND:
console.error('文件不存在:', error.message);
break;
case MDZErrorType.INVALID_FORMAT:
console.error('无效的MDZ格式:', error.message);
break;
default:
console.error('MDZ错误:', error.message);
}
} else {
console.error('未知错误:', error);
}
}
}批量资源处理
处理大量资源文件的示例:
import { MDZFile } from 'markdownzip';
import * as fs from 'fs';
import * as path from 'path';
async function batchAssetProcessing() {
const mdz = new MDZFile('document.mdz');
await mdz.create('# 多资源文档');
// 读取目录中的所有图像文件
const imageDir = '/path/to/images';
const files = fs.readdirSync(imageDir)
.filter(file => ['.jpg', '.png', '.gif'].includes(path.extname(file).toLowerCase()));
// 添加所有图像
for (const file of files) {
const filePath = path.join(imageDir, file);
const assetId = await mdz.addAsset(filePath);
console.log(`添加资源: ${file}, ID: ${assetId}`);
}
// 更新内容,包含所有图像的引用
const imageReferences = files.map(file =>
``
).join('\n\n');
await mdz.setMainContent(`# 多资源文档\n\n${imageReferences}`);
await mdz.save();
await mdz.close();
}导出与转换
将MDZ文件导出为其他格式:
import { MDZFile } from 'markdownzip';
import * as fs from 'fs';
import * as path from 'path';
async function exportMDZ() {
const mdz = new MDZFile('example.mdz');
await mdz.open();
// 创建导出目录
const exportDir = './export';
if (!fs.existsSync(exportDir)) {
fs.mkdirSync(exportDir, { recursive: true });
}
// 导出主内容
const content = await mdz.getMainContent();
fs.writeFileSync(path.join(exportDir, 'content.md'), content);
// 导出所有资源
const assets = mdz.getAssets();
const assetsDir = path.join(exportDir, 'assets');
if (!fs.existsSync(assetsDir)) {
fs.mkdirSync(assetsDir, { recursive: true });
}
for (const asset of assets) {
await mdz.extractAsset(asset.id, path.join(assetsDir, asset.originalName));
}
// 导出元数据
const metadata = mdz.getMetadata();
fs.writeFileSync(
path.join(exportDir, 'metadata.json'),
JSON.stringify(metadata, null, 2)
);
await mdz.close();
console.log(`已导出到 ${exportDir}`);
}安全特性
MDZ格式具有多项安全特性:
- 密码保护 - 使用AES-256-CBC加密算法保护敏感内容
- 数据完整性 - 使用SHA-256校验和验证文件未被篡改
- 安全的路径处理 - 防止目录遍历攻击
- 签名验证 - 验证MDZ文件格式的有效性
加密详情
MDZ使用以下加密实现:
- 使用PBKDF2进行密钥派生,增强密码强度
- 使用随机盐和初始化向量(IV)确保加密安全性
- 加密结果包含算法信息、盐值和IV,便于未来格式升级
兼容性与性能
- 支持Node.js 14及以上版本
- 支持处理大型文件(>1GB)和大量资源
- 所有文件操作均支持异步处理,避免阻塞主线程
贡献
欢迎贡献代码、提交问题或改进建议。提交PR前请确保:
- 代码遵循项目的代码风格
- 添加适当的测试用例
- 更新相关文档
许可证
MIT
