node如何读取excel表格内的图片信息

493 阅读2分钟

在 Node.js 中读取 Excel 表格内的图片信息并保存图片,需要使用一个能够处理 Excel 文件的库和一个能够解析图片的库,如exceljs

npm i exceljs
    
async function processExcelFile(filePath, sheetName) {
    const workbook = new ExcelJS.Workbook();
    await workbook.xlsx.readFile(filePath);
    // 获取指定的工作表
    const sheet = workbook.getWorksheet(sheetName);
    let imagesData = {};
    // 遍历工作表中的所有图片
    sheet.getImages().forEach((image, index) => {
        const imageId = image.imageId;
        const imageData = workbook.getImage(imageId);
        if (imageData) {
            const buffer = imageData.buffer;
            const ext = imageData.extension || "png"; // 默认扩展名为 png
            const positionKey = `${image.range.tl.row}-${image.range.tl.col}`;

            if (!imagesData[positionKey]) {
                imagesData[positionKey] = {};
            }
            // 保存图片信息
            imagesData[positionKey] = {
                buffer,
                ext,
                filename: `image-${positionKey}.${ext}`,
                cellAddress: { row: image.range.tl.row, col: image.range.tl.col },
            };
        }
    });
    return imagesData;
 }

如果在使用 exceljs 库读取 Excel 文件时遇到失败的问题,可能是文件格式或内容不符合规范。建议使用其他工具(如 Microsoft Excel 或 LibreOffice)打开该文件,以确认文件本身没有损坏,并保存一次,能解决大部分问题。文件没损坏的情况下,可使用jszipxml2js实现。

async function extractImagesFromExcel(filePath, fromBuffer = false) {
    let buffer;
    if (!fromBuffer) {
        // 读取 Excel 文件的数据
        buffer = await fs.promises.readFile(filePath);
    } else {
        buffer = filePath;
    }
    // 使用 JSZip 加载 Excel 文件 (实际上是一个 zip 压缩包)
    const zip = await JSZip.loadAsync(buffer);
    // 定义工作簿 XML 的路径
    const workbookRelsPath = "xl/workbook.xml";
    // 获取工作簿 XML 数据
    const workbookData = await zip.file(workbookRelsPath).async("string");
    // 解析工作簿 XML 数据
    const workbookXml = await xml2js.parseStringPromise(workbookData);
    // 获取所有工作表信息
    const sheets = workbookXml.workbook.sheets[0].sheet;
    // console.log("sheets", sheets);
    // 图片集合
    let imagesData = {};
    // 遍历每个工作表
    for (let sheet of sheets) {
        // 获取工作表 ID 和名称
        const sheetId = sheet.$.sheetId || sheet.$["r:id"];
        const sheetName = sheet.$.name;
        // 定义工作表关系文件的路径
        const sheetRelsPath = `xl/worksheets/_rels/sheet${sheetId.replace("rId", "")}.xml.rels`;
        // 如果没有找到关系文件,则跳过
        if (!zip.files[sheetRelsPath]) continue;
        // 获取工作表关系数据
        const sheetRelsData = await zip.file(sheetRelsPath).async("string");
        // 解析工作表关系数据
        const sheetRelsXml = await xml2js.parseStringPromise(sheetRelsData);
        // 筛选出类型为 "drawing" 的关系
        const drawingRels = sheetRelsXml.Relationships.Relationship.filter((rel) => rel.$.Type.includes("drawing"));
        // 遍历每个绘图关系
        for (let drawingRel of drawingRels) {
            // 定义绘图文件的路径
            const drawingPath = `xl/drawings/${drawingRel.$.Target.replace("../drawings/", "")}`;
            // 获取绘图数据
            const drawingData = await zip.file(drawingPath).async("string");
            // 解析绘图数据
            const drawingXml = await xml2js.parseStringPromise(drawingData);
            // 获取对应的绘图 .rels 文件路径
            const drawingRelsPath = `xl/drawings/_rels/${path.basename(drawingPath)}.rels`;
            // 如果没有找到绘图关系文件,则跳过
            if (!zip.files[drawingRelsPath]) continue;
            // 获取绘图关系数据
            const drawingRelsData = await zip.file(drawingRelsPath).async("string");
            // 解析绘图关系数据
            const drawingRelsXml = await xml2js.parseStringPromise(drawingRelsData);
            // 获取一个单元格锚点或者两个单元格锚点
            const twoCellAnchors = drawingXml["xdr:wsDr"]["xdr:oneCellAnchor"] || [];
            // 遍历每个锚点
            for (let anchor of twoCellAnchors) {
                // 获取锚点起始位置
                const from = anchor["xdr:from"][0];
                const row = parseInt(from["xdr:row"][0]);
                const col = parseInt(from["xdr:col"][0]);
                // 将列索引转换为字母
                // const colLetter = String.fromCharCode(65 + col);
                // 获取图片的关系 ID
                const imageRelId = anchor["xdr:pic"][0]["xdr:blipFill"][0]["a:blip"][0]["$"]["r:embed"];
                // 查找图片关系
                const imgRel = drawingRelsXml.Relationships.Relationship.find((rel) => rel.$.Id === imageRelId && rel.$.Type.includes("image"));
                // 如果找到图片关系
                if (imgRel) {
                    // 定义图片文件的路径
                    const imgPath = `xl/media/${imgRel.$.Target.replace("../media/", "")}`;
                    // 获取图片数据
                    const imgData = await zip.file(imgPath).async("nodebuffer");
                    // 获取图片扩展名
                    const imgExt = path.extname(imgPath) || ".png";
                    // 定义图片名称,包括工作表名称、列和行
                    const imgName = `${sheetName}-${col}-${row + 1}${imgExt}`;
                    imagesData[imgName] = {
                        buffer: imgData,
                        ext: imgExt,
                        fileName: imgName,
                        cellAddress: { row, col },
                    };
                    // 保存图片到当前目录
                    // fs.writeFileSync(path.join(__dirname, imgName), imgData);
                    // console.log(`Extracted ${imgName}`);
                }
            }
        }
    }
    return imagesData;
}