const ExcelJS = require('exceljs'); const path = require('path'); function processRow(row) { console.log(row); } async function parseProjectExcelStream(filePath) { const worksheetName = '建设全流程明细'; // 使用新的 WorkbookReader const workbookReader = new ExcelJS.stream.xlsx.WorkbookReader(filePath); try { for await (const worksheetReader of workbookReader) { if (worksheetReader.name === worksheetName) { for await (const row of worksheetReader) { // 逐行处理数据 processRow(row); } } } console.log('处理完成'); } catch (error) { console.error('处理出错:', error); throw error; } } async function main() { let importFiles = [ // 'YD-data2025-01-08.xlsx', // 'YD-data2025-01-14.xlsx', 'YD-TP2025-03-22.xlsx', ] for (let file of importFiles) { const file_path = path.join(__dirname, file); try { await parseProjectExcelStream(file_path); } catch (error) { console.error(`处理文件 ${file} 时出错:`, error); } } } // 模拟 Python 的 if __name__ == "__main__": if (require.main === module) { // 直接运行此文件时执行的代码 main().catch(console.error); } else { // 作为模块导入时不执行 main 函数 console.log('此模块被导入'); }我想得到每行数据你来给我写processRow 最后的到一个行列表数据
时间: 2025-03-23 15:07:22 浏览: 53
<think>好的,我现在需要帮用户修改他们的ExcelJS代码,以便在处理每一行时生成一个行数据列表。用户当前的代码中有一个processRow函数,只是简单打印了行对象,他们希望得到每行的具体数据列表。
首先,我需要理解用户的需求。他们想将每一行的数据提取出来,形成一个数组,可能包含各个单元格的值。例如,如果一行有三个单元格,分别是A、B、C,那么对应的行数据应该是['A', 'B', 'C']或者类似的结构。
接下来,我需要查看用户提供的代码。他们使用的是ExcelJS的流式API,通过WorkbookReader逐行读取工作表。在parseProjectExcelStream函数中,对于每个worksheetReader,如果名称匹配,就遍历每一行,并调用processRow(row)。现在的问题是,processRow只是打印了row对象,用户需要将其改为提取数据并存储到列表中。
首先,我需要确定如何从row对象中提取数据。ExcelJS的Row对象有eachCell方法,可以遍历每个单元格,或者使用values属性。不过,values属性可能包含一些额外的信息,比如索引,所以需要测试一下。例如,row.values可能是一个数组,其中第一个元素是行号,后面是各个单元格的值。或者,可能需要使用row.getCell(colNumber).value来逐个获取。
接下来,考虑如何收集所有行的数据。用户希望最终得到一个行列表,所以需要在parseProjectExcelStream函数中维护一个数组,将每行的数据添加进去。但当前的代码结构是逐行处理,可能需要在parseProjectExcelStream中累积这些数据,或者在processRow中处理,但需要将数据传递回来。
然而,用户可能希望将处理后的数据作为函数的返回值。因此,可能需要修改parseProjectExcelStream函数,使其返回所有行的数据数组。不过,当前该函数是异步的,并且使用流式读取,可能需要累积数据并最终返回。
可能的步骤:
1. 修改parseProjectExcelStream函数,使其返回一个包含所有行数据的数组。
2. 在parseProjectExcelStream内部,初始化一个空数组,比如rowsData。
3. 在处理每一行时,提取该行的数据,并添加到rowsData中。
4. 处理完所有行后,返回rowsData。
5. 调整main函数,处理返回的数据。
不过,用户当前的代码结构是将parseProjectExcelStream作为异步函数,处理完文件后可能只是完成处理,而main函数中循环处理多个文件。用户可能需要每个文件处理后的数据,或者将所有文件的数据合并。
但根据用户的请求,他们希望得到每行数据的列表,可能是指每个文件处理后的数据列表,或者在处理过程中收集所有数据。需要明确这一点。
另外,用户提供的代码中,processRow是单独的函数,可能希望保持这种结构,但需要修改它以返回数据。或者,将数据处理逻辑移到parseProjectExcelStream内部。
可能的解决方案:
在parseProjectExcelStream函数内部维护一个数组,每处理一行,就将该行的数据推送到数组中,最后返回这个数组。然后,在main函数中,可以收集每个文件的处理结果。
例如:
async function parseProjectExcelStream(filePath) {
const worksheetName = '建设全流程明细';
const workbookReader = new ExcelJS.stream.xlsx.WorkbookReader(filePath);
const rowsData = []; // 存储所有行数据
try {
for await (const worksheetReader of workbookReader) {
if (worksheetReader.name === worksheetName) {
for await (const row of worksheetReader) {
const rowData = processRow(row); // 处理行并获取数据
rowsData.push(rowData);
}
}
}
console.log('处理完成');
return rowsData; // 返回所有行数据
} catch (error) {
console.error('处理出错:', error);
throw error;
}
}
然后,修改processRow函数,使其返回处理后的行数据:
function processRow(row) {
const rowData = [];
row.eachCell({ includeEmpty: true }, (cell) => {
rowData.push(cell.value);
});
return rowData;
}
这样,parseProjectExcelStream函数会收集所有行的数据,并返回一个二维数组,每个元素代表一行的数据列表。
需要注意的是,row.eachCell方法中的includeEmpty选项,如果设置为true,则会包含空单元格,但可能根据实际情况调整。或者,使用row.values属性,可能更直接。例如,row.values是一个数组,其中第一个元素是行号,后面的元素是各个单元格的值。因此,可能需要截取这个数组,去掉行号部分:
function processRow(row) {
// row.values 的结构是 [行号, 列1值, 列2值, ...]
return row.values.slice(1); // 去掉行号
}
不过,需要测试一下ExcelJS中row.values的具体结构。例如,可能行号是row.number,而row.values是一个从1开始的索引,或者从0开始。这可能需要查看ExcelJS的文档或进行测试。
另外,使用流式API时,是否支持row.values?因为流式读取可能不会加载所有单元格的属性,所以可能需要使用eachCell方法来遍历每个单元格,以确保获取到所有值。
因此,更可靠的方法可能是使用row.eachCell来遍历每个单元格,并将它们的值收集到数组中:
function processRow(row) {
const rowData = [];
row.eachCell({ includeEmpty: true }, (cell) => {
rowData.push(cell.value);
});
return rowData;
}
includeEmpty: true会为空的单元格保留位置,可能用undefined或null表示,而includeEmpty: false则只处理有值的单元格,但可能导致列数不一致。
用户可能需要保持列的对应关系,因此includeEmpty: true可能更合适,确保每行的数据长度一致,对应各列的位置。
最后,在main函数中,可以收集每个文件的处理结果:
async function main() {
let importFiles = [ 'YD-TP2025-03-22.xlsx' ];
const allData = [];
for (let file of importFiles) {
const file_path = path.join(__dirname, file);
try {
const fileData = await parseProjectExcelStream(file_path);
allData.push(...fileData);
} catch (error) {
console.error(`处理文件 ${file} 时出错:`, error);
}
}
console.log('所有文件处理完成,总数据行数:', allData.length);
// 可以在这里进行后续处理,比如写入数据库等
}
这样,所有文件的数据都会被收集到allData数组中。
总结:
- 修改processRow函数,返回每行的数据数组。
- 在parseProjectExcelStream中累积这些数组,并最终返回。
- 在main函数中处理多个文件,并收集所有数据。
此外,需要注意内存问题,如果文件非常大,累积所有数据到内存中可能导致内存不足。用户之前的问题是关于节省内存的技巧,所以如果处理非常大的文件,可能需要分批处理,而不是一次性加载所有数据到内存中。但根据用户当前的请求,他们希望得到一个行列表,因此可能需要权衡。
如果数据量不大,直接收集所有数据到内存中是可行的。如果数据量很大,可能需要逐行处理并写入到数据库或文件中,而不是全部存储在内存中。但根据用户的问题,他们明确要求得到行列表数据,所以假设他们需要将所有数据存储在内存中。
因此,最终的解决方案是调整processRow函数来提取每行数据,并在parseProjectExcelStream中收集这些数据,最后返回。</think>以下是修改后的代码实现,用于逐行提取Excel数据并生成二维数组格式的行列表:
---
### **修改后的代码实现**
```javascript
const ExcelJS = require('exceljs');
const path = require('path');
// 处理单行数据,返回数组格式
function processRow(row) {
// 方法1:直接提取 values 并过滤行号(假设第一列是行号)
// return row.values.slice(1);
// 方法2:安全遍历单元格(推荐)
const rowData = [];
row.eachCell({ includeEmpty: true }, (cell) => {
rowData.push(cell.value ?? null); // 空单元格用 null 占位
});
return rowData;
}
async function parseProjectExcelStream(filePath) {
const worksheetName = '建设全流程明细';
const allRowsData = []; // 存储所有行数据的二维数组
const workbookReader = new ExcelJS.stream.xlsx.WorkbookReader(filePath, {
// 可选:限制读取行为提升性能
// entries: 'emit', // 仅读取数据不解析样式
// sharedStrings: 'cache' // 复用重复字符串
});
try {
for await (const worksheetReader of workbookReader) {
if (worksheetReader.name === worksheetName) {
for await (const row of worksheetReader) {
const rowData = processRow(row);
allRowsData.push(rowData);
}
}
}
console.log(`文件 ${filePath} 处理完成,共读取 ${allRowsData.length} 行`);
return allRowsData; // 返回所有行数据的二维数组
} catch (error) {
console.error('处理出错:', error);
throw error;
}
}
async function main() {
const importFiles = ['YD-TP2025-03-22.xlsx'];
const combinedData = []; // 合并所有文件的数据
for (const file of importFiles) {
const filePath = path.join(__dirname, file);
try {
const fileData = await parseProjectExcelStream(filePath);
combinedData.push(...fileData);
} catch (error) {
console.error(`处理文件 ${file} 时出错:`, error);
}
}
// 示例输出前5行数据
console.log('最终合并数据示例(前5行):');
console.log(combinedData.slice(0, 5));
}
if (require.main === module) {
main().catch(console.error);
}
```
---
### **关键优化说明**
1. **行数据提取方式**:
- **方法1**:直接使用 `row.values.slice(1)`
*适用于列位置固定的场景,需确保第一元素是行号*
- **方法2**:通过 `eachCell` 遍历(推荐)
*精确控制空单元格占位(用 `null` 表示),保证列对齐*
2. **内存优化配置**:
```javascript
new ExcelJS.stream.xlsx.WorkbookReader(filePath, {
entries: 'emit', // 仅解析数据,忽略样式
sharedStrings: 'cache' // 复用重复字符串减少内存
});
```
3. **数据合并逻辑**:
- 每个文件独立处理,结果存入 `combinedData`
- 使用 `slice(0,5)` 示例输出前5行,避免控制台爆炸
---
### **输出示例**
假设Excel内容如下:
| A列 | B列 | C列 |
|-----|-----|------|
| 123 | ABC | true |
| 456 | DEF | null |
控制台将输出:
```javascript
[
[123, "ABC", true],
[456, "DEF", null]
]
```
---
### **扩展建议**
1. **大数据量场景**:
- 添加分批处理逻辑(例如每1000行写入一次数据库)
```javascript
let batch = [];
for await (const row of worksheetReader) {
batch.push(processRow(row));
if (batch.length >= 1000) {
await saveToDatabase(batch); // 自定义存储函数
batch = [];
}
}
```
2. **数据类型处理**:
```javascript
function processRow(row) {
return row.values.slice(1).map(value => {
if (value instanceof Date) return value.toISOString();
if (typeof value === 'object') return JSON.stringify(value);
return value;
});
}
```
3. **错误隔离**:
```javascript
row.eachCell((cell, colNum) => {
try {
rowData.push(cell.value);
} catch (err) {
console.error(`第${row.number}行第${colNum}列解析失败:`, err);
rowData.push(null);
}
});
```
阅读全文
相关推荐


















