Node.js 导出 Excel 完整指南:表头样式、合并单元格、图片插入与自动扩展封装
在实际业务开发中,Excel 导出几乎是后台系统的标配能力,尤其在电商、报表、数据分析等场景中,对格式和功能的要求往往非常高,例如:
- 自定义表头样式(颜色、字体、对齐)
- 合并单元格(多级表头、说明行)
- 插入图片(商品图、头像等)
- 支持多种图片来源(本地 / URL / Base64)
- 自动适配布局
本文将从基础能力入手,逐步实现一个工程化的 Excel 导出方案,并最终抽象出一个通用的 addImageAuto 方法。可能你并不需要复杂的功能,那你可以抽取出其中的关键代码自我实现功能。
![]()
一、基础能力:使用 ExcelJS 导出 Excel
安装依赖:
npm install exceljs基础示例:
const ExcelJS = require('exceljs');
async function exportExcel() {
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet('Sheet1');
// 定义列
worksheet.columns = [
{ header: '姓名', key: 'name', width: 20 },
{ header: '年龄', key: 'age', width: 10 },
{ header: '地址', key: 'address', width: 30 },
];
// 表头样式
const headerRow = worksheet.getRow(1);
headerRow.eachCell((cell) => {
cell.font = {
bold: true,
color: { argb: 'FFFFFFFF' },
};
cell.alignment = {
horizontal: 'center',
vertical: 'middle',
};
cell.fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FF4472C4' },
};
});
// 数据
worksheet.addRow({ name: '张三', age: 18, address: '北京' });
worksheet.addRow({ name: '李四', age: 22, address: '上海' });
// 合并单元格
worksheet.mergeCells('A4:C4');
worksheet.getCell('A4').value = '合并单元格示例';
await workbook.xlsx.writeFile('output.xlsx');
}
exportExcel();二、插入图片:基础实现
const imageId = workbook.addImage({
filename: './logo.png',
extension: 'png',
});
worksheet.addImage(imageId, {
tl: { col: 0, row: 5 },
ext: { width: 200, height: 100 },
});问题在于: extension 需要手动指定,这在动态场景下不可靠。
三、自动识别图片扩展名
1. 基于文件路径
const path = require('path');
function getImageExtension(filePath) {
const ext = path.extname(filePath).toLowerCase().replace('.', '');
const map = {
jpg: 'jpeg',
jpeg: 'jpeg',
png: 'png',
gif: 'gif',
};
if (!map[ext]) {
throw new Error(`不支持的图片格式: ${ext}`);
}
return map[ext];
}2. 基于 Buffer(文件头判断)
function getImageExtensionFromBuffer(buffer) {
if (buffer.slice(0, 4).toString('hex') === '89504e47') {
return 'png';
}
if (buffer.slice(0, 3).toString('hex') === 'ffd8ff') {
return 'jpeg';
}
if (buffer.slice(0, 3).toString() === 'GIF') {
return 'gif';
}
throw new Error('无法识别图片格式');
}四、工程化封装:addImageAuto
为了支持多种输入来源(路径 / URL / Buffer / Base64),可以封装统一入口。
核心实现
const fs = require('fs');
const path = require('path');
const axios = require('axios');
// 自动识别扩展名
function detectExtension(input, buffer) {
if (typeof input === 'string') {
if (input.startsWith('data:image')) {
const match = input.match(/^data:image\/(\w+);base64,/);
return match ? match[1] : 'png';
}
const ext = path.extname(input).toLowerCase().replace('.', '');
if (ext) {
return ext === 'jpg' ? 'jpeg' : ext;
}
}
if (buffer) {
const hex = buffer.slice(0, 4).toString('hex');
if (hex.startsWith('89504e47')) return 'png';
if (hex.startsWith('ffd8ff')) return 'jpeg';
if (buffer.slice(0, 3).toString() === 'GIF') return 'gif';
}
return 'png';
}
// 加载图片
async function loadImage(input) {
if (Buffer.isBuffer(input)) {
return { buffer: input };
}
if (typeof input === 'string' && input.startsWith('data:image')) {
const base64 = input.split(',')[1];
return { buffer: Buffer.from(base64, 'base64') };
}
if (typeof input === 'string' && /^https?:\/\//.test(input)) {
const res = await axios.get(input, { responseType: 'arraybuffer' });
return { buffer: Buffer.from(res.data) };
}
if (typeof input === 'string') {
return { filename: input };
}
throw new Error('不支持的图片类型');
}
// 主方法
async function addImageAuto(workbook, worksheet, input, options = {}) {
const { buffer, filename } = await loadImage(input);
const extension = detectExtension(input, buffer);
const imageId = workbook.addImage({
...(buffer ? { buffer } : { filename }),
extension,
});
if (options.range) {
worksheet.addImage(imageId, options.range);
} else {
worksheet.addImage(imageId, {
tl: options.tl || { col: 0, row: 0 },
ext: options.ext || { width: 100, height: 100 },
});
}
return imageId;
}五、多场景使用示例
本地图片
await addImageAuto(workbook, worksheet, './logo.png', {
tl: { col: 0, row: 5 },
ext: { width: 200, height: 100 },
});URL 图片
await addImageAuto(
workbook,
worksheet,
'https://example.com/logo.jpg',
{
range: 'B6:D10',
}
);Buffer
const buffer = fs.readFileSync('./logo.png');
await addImageAuto(workbook, worksheet, buffer, {
tl: { col: 2, row: 5 },
});Base64
const base64 = 'data:image/png;base64,...';
await addImageAuto(workbook, worksheet, base64, {
tl: { col: 4, row: 5 },
});六、自动适配单元格尺寸
function fitCellToImage(worksheet, col, row, width, height) {
worksheet.getColumn(col + 1).width = width / 7;
worksheet.getRow(row + 1).height = height * 0.75;
}七、总结
通过以上步骤,已经实现了一套完整的 Excel 导出能力:
- 表头样式控制(颜色、字体、对齐)
- 合并单元格
- 图片插入
- 自动识别图片类型
- 支持多种图片输入方式
- 通用方法封装(addImageAuto)
- 自动适配布局
该方案适用于大多数业务导出场景,并具备良好的扩展性,可以进一步构建复杂报表或商品导出系统。
发布评论
发布评论前请先 登录。
0 评论
点赞
分享
收藏
评论列表 0

暂无评论


