You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用node-stream-zip解析manifest.json报错,寻求替代提取方案

解决zip包内manifest.json解析错误及替代提取方法

问题背景

使用node-stream-zip库读取zip包内的manifest.json时,出现以下错误:

错误信息 : SyntaxError: Unexpected token in JSON at position 0
at JSON.parse ()
at StreamZip.

已验证manifest.json格式正确,但同步读取解析时失败,以下是几种可行的替代提取方法:

前置排查点

在更换方法前,先排查两个常见问题:

  • BOM头问题:部分JSON文件开头带有UTF-8 BOM(\ufeff),直接toString('utf8')会保留该字符,导致JSON解析失败。可以先去除:
    const content = zip.entryDataSync("manifest.json").toString('utf8').replace(/^\uFEFF/, '');
    const zipDotTxtContents = JSON.parse(content);
    
  • entry路径大小写:zip内的文件路径可能区分大小写,确认"manifest.json"的路径和大小写完全匹配。

替代提取方法

方法1:改用异步读取entry数据

避免同步方法可能的潜在问题,使用异步方式读取:

const getAppInfoFromManifest = async (productId) => {
    return new Promise((resolve, reject) => {
        const zip = new StreamZip({
            file: `./files/package${productId}.zip`,
            storeEntries: true,
        });

        zip.on("ready", () => {
            zip.entryData("manifest.json", (err, data) => {
                if (err) {
                    console.log(`读取manifest失败: ${err}`);
                    return resolve(undefined);
                }
                try {
                    const content = data.toString('utf8').replace(/^\uFEFF/, '');
                    const manifest = JSON.parse(content);
                    zip.close();
                    resolve({
                        appId: manifest?.id,
                        appVersionManifest: manifest?.version
                    });
                } catch (parseErr) {
                    console.log(`解析manifest失败: ${parseErr}`);
                    zip.close();
                    resolve(undefined);
                }
            });
        });

        zip.on("error", (err) => {
            console.log(err);
            console.log(`Manifest Parsing Issue for ${productId}`);
            resolve(undefined);
        });
    });
};

方法2:换用adm-zip库

adm-zip是另一个常用的Node.js zip处理库,API更直观:

const AdmZip = require('adm-zip');

const getAppInfoFromManifest = async (productId) => {
    try {
        const zip = new AdmZip(`./files/package${productId}.zip`);
        const manifestContent = zip.readAsText("manifest.json").replace(/^\uFEFF/, '');
        const manifest = JSON.parse(manifestContent);
        return {
            appId: manifest?.id,
            appVersionManifest: manifest?.version
        };
    } catch (err) {
        console.log(err);
        console.log(`Manifest Parsing Issue for ${productId}`);
        return undefined;
    }
};

方法3:先解压文件到临时目录再读取

如果zip包不大,可以先解压到临时目录,再直接读取文件:

const fs = require('fs').promises;
const path = require('path');
const AdmZip = require('adm-zip');
const os = require('os');

const getAppInfoFromManifest = async (productId) => {
    const tempDir = path.join(os.tmpdir(), `temp_${productId}`);
    try {
        const zip = new AdmZip(`./files/package${productId}.zip`);
        zip.extractAllTo(tempDir, true);
        
        const manifestPath = path.join(tempDir, "manifest.json");
        const content = await fs.readFile(manifestPath, 'utf8');
        const manifest = JSON.parse(content.replace(/^\uFEFF/, ''));
        
        // 清理临时文件
        await fs.rm(tempDir, { recursive: true, force: true });
        
        return {
            appId: manifest?.id,
            appVersionManifest: manifest?.version
        };
    } catch (err) {
        console.log(err);
        console.log(`Manifest Parsing Issue for ${productId}`);
        // 即使出错也清理临时文件
        await fs.rm(tempDir, { recursive: true, force: true }).catch(() => {});
        return undefined;
    }
};

方法4:使用unzipper库流式处理

unzipper支持流式解压,适合处理大文件:

const unzipper = require('unzipper');
const fs = require('fs');

const getAppInfoFromManifest = async (productId) => {
    return new Promise((resolve, reject) => {
        fs.createReadStream(`./files/package${productId}.zip`)
            .pipe(unzipper.Parse())
            .on('entry', async (entry) => {
                if (entry.path === "manifest.json") {
                    let content = '';
                    entry.on('data', (chunk) => content += chunk.toString('utf8'));
                    entry.on('end', () => {
                        try {
                            const manifest = JSON.parse(content.replace(/^\uFEFF/, ''));
                            resolve({
                                appId: manifest?.id,
                                appVersionManifest: manifest?.version
                            });
                        } catch (err) {
                            console.log(`解析manifest失败: ${err}`);
                            resolve(undefined);
                        }
                    });
                } else {
                    entry.autodrain(); // 跳过其他文件
                }
            })
            .on('error', (err) => {
                console.log(err);
                console.log(`Manifest Parsing Issue for ${productId}`);
                resolve(undefined);
            });
    });
};

内容的提问来源于stack exchange,提问作者Jagdish Padeliya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 03:39:21