excel-csv-handler 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,20 @@
1
+ # excel-csv-handler
2
+
3
+ A simple Node.js library to read and write Excel (`.xlsx`, `.xls`) and CSV files, with built-in GBK encoding support for Chinese users.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install excel-csv-handler
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```javascript
14
+ import ExcelCsvHandler from 'excel-csv-handler';
15
+
16
+ const handler = new ExcelCsvHandler();
17
+ await handler.write('output.csv', [{ name: '张三', age: 25 }]);
18
+ const data = await handler.read('output.csv');
19
+ console.log(data);
20
+ ```
Binary file
package/index.js ADDED
@@ -0,0 +1,172 @@
1
+ // excel-csv-handler.js
2
+ import * as XLSX from 'xlsx';
3
+ import * as fs from 'fs';
4
+ import * as path from 'path';
5
+ import iconv from 'iconv-lite';
6
+ import * as csv from 'fast-csv';
7
+ import { writeToString } from 'fast-csv';
8
+
9
+ class ExcelCsvHandler {
10
+ /**
11
+ * 读取 Excel 或 CSV 文件(支持 GBK 编码)
12
+ * @param {string} filePath - 文件路径
13
+ * @param {number} headerRow - 标题行索引(从 0 开始)
14
+ * @returns {Promise<Array<Object>>}
15
+ */
16
+ async read(filePath, headerRow = 0) {
17
+ const ext = path.extname(filePath).toLowerCase();
18
+
19
+ if (ext === '.xlsx' || ext === '.xls') {
20
+ return this.readExcelFile(filePath, headerRow);
21
+ } else if (ext === '.csv') {
22
+ return this.readCsvFile(filePath, headerRow);
23
+ } else {
24
+ throw new Error(`不支持的文件格式: ${ext}`);
25
+ }
26
+ }
27
+
28
+ /**
29
+ * 写入 Excel 或 CSV 文件(以 GBK 编码保存 CSV,Excel 保持默认)
30
+ * @param {string} filePath - 文件路径
31
+ * @param {Array<Object>} data - 要写入的数据
32
+ * @param {Array<string>} headers - 可选列顺序
33
+ */
34
+ async write(filePath, data, headers = null) {
35
+ const ext = path.extname(filePath).toLowerCase();
36
+
37
+ if (ext === '.xlsx' || ext === '.xls') {
38
+ this.writeExcelFile(filePath, data, headers);
39
+ } else if (ext === '.csv') {
40
+ await this.writeCsvFile(filePath, data, headers);
41
+ } else {
42
+ throw new Error(`不支持的文件格式: ${ext}`);
43
+ }
44
+ }
45
+
46
+ // ========= Excel 读写 =========
47
+
48
+ readExcelFile(filePath, headerRow) {
49
+ const workbook = XLSX.readFile(filePath);
50
+ const sheetName = workbook.SheetNames[0];
51
+ const worksheet = workbook.Sheets[sheetName];
52
+
53
+ // 读取原始行(数组形式)
54
+ const range = XLSX.utils.decode_range(worksheet['!ref'] || 'A1');
55
+ const rows = [];
56
+ for (let R = range.s.r; R <= range.e.r; ++R) {
57
+ const row = [];
58
+ for (let C = range.s.c; C <= range.e.c; ++C) {
59
+ const cellAddress = XLSX.utils.encode_cell({ r: R, c: C });
60
+ const cell = worksheet[cellAddress];
61
+ row.push(cell ? cell.v : '');
62
+ }
63
+ rows.push(row);
64
+ }
65
+
66
+ if (rows.length <= headerRow) return [];
67
+
68
+ const headers = rows[headerRow].map(String);
69
+ const dataRows = rows.slice(headerRow + 1);
70
+
71
+ return dataRows.map(row => {
72
+ const obj = {};
73
+ headers.forEach((key, idx) => {
74
+ obj[key] = idx < row.length ? String(row[idx] ?? '') : '';
75
+ });
76
+ return obj;
77
+ });
78
+ }
79
+
80
+ writeExcelFile(filePath, data, headers = null) {
81
+ if (!headers && data.length > 0) {
82
+ headers = Object.keys(data[0]);
83
+ } else if (!headers) {
84
+ headers = [];
85
+ }
86
+
87
+ const wsData = [headers, ...data.map(row => headers.map(h => row[h] ?? ''))];
88
+ const worksheet = XLSX.utils.aoa_to_sheet(wsData);
89
+ const workbook = XLSX.utils.book_new();
90
+ XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
91
+ XLSX.writeFile(workbook, filePath);
92
+ }
93
+
94
+ // ========= CSV 读写 =========
95
+
96
+ async readCsvFile(filePath, headerRow) {
97
+ return new Promise((resolve, reject) => {
98
+ const allRows = [];
99
+ const stream = fs.createReadStream(filePath)
100
+ .pipe(iconv.decodeStream('gbk'))
101
+ .pipe(csv.parse({ headers: false, skipLines: headerRow }));
102
+
103
+ stream.on('data', row => {
104
+ allRows.push(row);
105
+ });
106
+
107
+ stream.on('end', () => {
108
+ if (allRows.length === 0) {
109
+ resolve([]);
110
+ return;
111
+ }
112
+
113
+ // 第一行作为 headers
114
+ const headers = allRows[0].map(h => String(h ?? ''));
115
+ const dataRows = allRows.slice(1);
116
+
117
+ const result = dataRows.map(row => {
118
+ const obj = {};
119
+ headers.forEach((key, idx) => {
120
+ obj[key] = idx < row.length ? String(row[idx] ?? '') : '';
121
+ });
122
+ return obj;
123
+ });
124
+
125
+ resolve(result);
126
+ });
127
+
128
+ stream.on('error', reject);
129
+ });
130
+ }
131
+
132
+ async writeCsvFile(filePath, data, headers = null) {
133
+ if (!headers && data.length > 0) {
134
+ headers = Object.keys(data[0]);
135
+ } else if (!headers) {
136
+ headers = [];
137
+ }
138
+
139
+ const csvString = await writeToString(data, {
140
+ headers,
141
+ includeEndRowDelimiter: true,
142
+ rowDelimiter: '\r\n',
143
+ });
144
+
145
+ const gbkBuffer = iconv.encode(csvString, 'gbk');
146
+ fs.writeFileSync(filePath, gbkBuffer);
147
+ }
148
+ }
149
+
150
+ export default ExcelCsvHandler;
151
+
152
+ // 示例用法(取消注释可测试)
153
+
154
+ // (async () => {
155
+ // const handler = new ExcelCsvHandler();
156
+
157
+ // // 写回文件(自动用 GBK 编码)
158
+ // await handler.write('./output/result.csv', [
159
+ // { name: 'John Doe', age: 30 },
160
+ // { name: 'Jane Doe', age: 25 },
161
+ // ]);
162
+
163
+ // // 或写入 Excel
164
+ // await handler.write('./output/result.xlsx', [
165
+ // { name: 'John Doe', age: 30 },
166
+ // { name: 'Jane Doe', age: 25 },
167
+ // ]);
168
+
169
+ // // 读取文件(假设标题在第 1 行,即索引 0)
170
+ // const data = await handler.read('./output/result.csv', 0);
171
+ // console.log(data);
172
+ // })();
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "excel-csv-handler",
3
+ "version": "1.0.0",
4
+ "description": "A Node.js utility to read/write Excel and CSV files with GBK encoding support",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "test": "echo \"Error: no test specified\" && exit 1"
9
+ },
10
+ "keywords": [
11
+ "excel",
12
+ "csv",
13
+ "xlsx",
14
+ "gbk",
15
+ "node",
16
+ "file"
17
+ ],
18
+ "author": "Chao_bei",
19
+ "license": "MIT",
20
+ "dependencies": {
21
+ "fast-csv": "^5.0.5",
22
+ "iconv-lite": "^0.7.0",
23
+ "xlsx": "^0.18.5"
24
+ }
25
+ }