Skip to content

Commit f9249c0

Browse files
authored
Merge pull request #1 from zurmokeeper/feature/add_decryption_func
Feature/add decryption func
2 parents b213631 + 5c62b82 commit f9249c0

10 files changed

+175
-35
lines changed

.eslintrc

-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
"node": true
1414
},
1515
"rules": {
16-
"import/extensions": ["error", { "ignorePackages": true }],
1716
"arrow-parens": ["error", "as-needed"],
1817
"class-methods-use-this": ["off"],
1918
"comma-dangle": ["error", {"arrays": "always-multiline", "objects": "always-multiline", "imports": "always-multiline", "exports": "always-multiline", "functions": "never"}],

README.md

+12
Original file line numberDiff line numberDiff line change
@@ -2159,18 +2159,30 @@ faster or more resilient.
21592159
// read from a file
21602160
const workbook = new Excel.Workbook();
21612161
await workbook.xlsx.readFile(filename);
2162+
2163+
// read from a file, decrypt excel files encrypted with password
2164+
const workbook = new Excel.Workbook();
2165+
await workbook.xlsx.readFile(filename, {password:'123456'});
21622166
// ... use workbook
21632167

21642168

21652169
// read from a stream
21662170
const workbook = new Excel.Workbook();
21672171
await workbook.xlsx.read(stream);
2172+
2173+
// read from a stream, decrypt excel files encrypted with password
2174+
const workbook = new Excel.Workbook();
2175+
await workbook.xlsx.read(stream, {password:'123456'});
21682176
// ... use workbook
21692177

21702178

21712179
// load from buffer
21722180
const workbook = new Excel.Workbook();
21732181
await workbook.xlsx.load(data);
2182+
2183+
// load from buffer, decrypt excel files encrypted with password
2184+
const workbook = new Excel.Workbook();
2185+
await workbook.xlsx.load(data, {password:'123456'});
21742186
// ... use workbook
21752187
```
21762188

README_zh.md

+12
Original file line numberDiff line numberDiff line change
@@ -2049,18 +2049,30 @@ worksheet.unprotect();
20492049
// 从文件读取
20502050
const workbook = new Excel.Workbook();
20512051
await workbook.xlsx.readFile(filename);
2052+
2053+
// 从文件读取, 解密使用密码加密的excel文件
2054+
const workbook = new Excel.Workbook();
2055+
await workbook.xlsx.readFile(filename, {password:'123456'});
20522056
// ... 使用 workbook
20532057

20542058

20552059
// 从流读取
20562060
const workbook = new Excel.Workbook();
20572061
await workbook.xlsx.read(stream);
2062+
2063+
// 从流读取, 解密使用密码加密的excel文件
2064+
const workbook = new Excel.Workbook();
2065+
await workbook.xlsx.read(stream, {password:'123456'});
20582066
// ... 使用 workbook
20592067

20602068

20612069
// 从 buffer 加载
20622070
const workbook = new Excel.Workbook();
20632071
await workbook.xlsx.load(data);
2072+
2073+
// 从 buffer 加载, 解密使用密码加密的excel文件
2074+
const workbook = new Excel.Workbook();
2075+
await workbook.xlsx.load(data, {password:'123456'});
20642076
// ... 使用 workbook
20652077
```
20662078

index.d.ts

+29-3
Original file line numberDiff line numberDiff line change
@@ -1453,23 +1453,49 @@ export interface XlsxWriteOptions extends stream.xlsx.WorkbookWriterOptions {
14531453
zip: Partial<JSZipGeneratorOptions>;
14541454
}
14551455

1456+
export interface XlsxReadOptions {
1457+
/**
1458+
* @desc Decrypted passwords, maximum length is 255
1459+
* optional
1460+
*/
1461+
password: string;
1462+
1463+
/**
1464+
* @desc This parameter indicates that the input is a base64-encoded buffer. Only valid for the load method
1465+
* optional
1466+
*/
1467+
base64: boolean;
1468+
1469+
/**
1470+
* @desc TODO:
1471+
* optional
1472+
*/
1473+
maxRows: number;
1474+
1475+
/**
1476+
* @desc TODO:
1477+
* optional
1478+
*/
1479+
maxCols: number;
1480+
}
1481+
14561482
export interface Xlsx {
14571483
/**
14581484
* read from a file
14591485
*/
1460-
readFile(path: string): Promise<Workbook>;
1486+
readFile(path: string, options?: Partial<XlsxReadOptions>): Promise<Workbook>;
14611487

14621488
/**
14631489
* read from a stream
14641490
* @param stream
14651491
*/
1466-
read(stream: import('stream').Stream): Promise<Workbook>;
1492+
read(stream: import('stream').Stream, options?: Partial<XlsxReadOptions>): Promise<Workbook>;
14671493

14681494
/**
14691495
* load from an array buffer
14701496
* @param buffer
14711497
*/
1472-
load(buffer: Buffer): Promise<Workbook>;
1498+
load(buffer: Buffer, options?: Partial<XlsxReadOptions>): Promise<Workbook>;
14731499

14741500
/**
14751501
* write to a buffer

lib/xlsx/xlsx.js

+18-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const fs = require('fs');
22
const JSZip = require('jszip');
33
const {PassThrough} = require('readable-stream');
4+
const officeCrypto = require('officecrypto-tool');
45
const ZipStream = require('../utils/zip-stream');
56
const StreamBuf = require('../utils/stream-buf');
67

@@ -22,6 +23,7 @@ const TableXform = require('./xform/table/table-xform');
2223
const CommentsXform = require('./xform/comment/comments-xform');
2324
const VmlNotesXform = require('./xform/comment/vml-notes-xform');
2425

26+
// eslint-disable-next-line import/extensions
2527
const theme1Xml = require('./xml/theme1.js');
2628

2729
function fsReadFileAsync(filename, options) {
@@ -276,6 +278,10 @@ class XLSX {
276278
vmlDrawings: {},
277279
};
278280

281+
if (options && options.password) {
282+
buffer = await officeCrypto.decrypt(buffer, {password: options.password});
283+
}
284+
279285
const zip = await JSZip.loadAsync(buffer);
280286
for (const entry of Object.values(zip.files)) {
281287
/* eslint-disable no-await-in-loop */
@@ -285,9 +291,11 @@ class XLSX {
285291
entryName = entryName.substr(1);
286292
}
287293
let stream;
288-
if (entryName.match(/xl\/media\//) ||
294+
if (
295+
entryName.match(/xl\/media\//) ||
289296
// themes are not parsed as stream
290-
entryName.match(/xl\/theme\/([a-zA-Z0-9]+)[.]xml/)) {
297+
entryName.match(/xl\/theme\/([a-zA-Z0-9]+)[.]xml/)
298+
) {
291299
stream = new PassThrough();
292300
stream.write(await entry.async('nodebuffer'));
293301
} else {
@@ -597,8 +605,7 @@ class XLSX {
597605
model.created = model.created || new Date();
598606
model.modified = model.modified || new Date();
599607

600-
model.useSharedStrings =
601-
options.useSharedStrings !== undefined ? options.useSharedStrings : true;
608+
model.useSharedStrings = options.useSharedStrings !== undefined ? options.useSharedStrings : true;
602609
model.useStyles = options.useStyles !== undefined ? options.useStyles : true;
603610

604611
// Manage the shared strings
@@ -673,11 +680,13 @@ class XLSX {
673680
reject(error);
674681
});
675682

676-
this.write(stream, options).then(() => {
677-
stream.end();
678-
}).catch(err=>{
679-
reject(err);
680-
});
683+
this.write(stream, options)
684+
.then(() => {
685+
stream.end();
686+
})
687+
.catch(err => {
688+
reject(err);
689+
});
681690
});
682691
}
683692

package.json

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@zurmokeeper/exceljs",
3-
"version": "4.3.0",
3+
"version": "4.4.0",
44
"description": "Excel Workbook Manager - Read and Write xlsx and csv Files.",
55
"private": false,
66
"license": "MIT",
@@ -93,13 +93,17 @@
9393
"views",
9494
"frozen",
9595
"split",
96-
"pageSetup"
96+
"pageSetup",
97+
"cfb",
98+
"ecma376_standard",
99+
"ecma376_agile"
97100
],
98101
"dependencies": {
99102
"archiver": "^5.0.0",
100103
"dayjs": "^1.8.34",
101104
"fast-csv": "^4.3.1",
102105
"jszip": "^3.10.1",
106+
"officecrypto-tool": "0.0.3",
103107
"readable-stream": "^3.6.0",
104108
"saxes": "^5.0.1",
105109
"tmp": "^0.2.0",
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
const ExcelJS = verquire('exceljs');
2+
const fs = require('fs');
3+
4+
const TEST_2247_STD_XLSX_FILE_NAME =
5+
'./spec/integration/data/emca376-standard-123456.xlsx';
6+
const TEST_2247_AGILE_XLSX_FILE_NAME =
7+
'./spec/integration/data/emca376-agile-123456.xlsx';
8+
9+
describe('pr related issues', () => {
10+
describe('pr add the function of reading encrypted xlsx ', () => {
11+
it('workbook.xlsx.readFile, ecma376_standard encryption method decrypted successfully', async () => {
12+
const workbook = new ExcelJS.Workbook();
13+
await workbook.xlsx.readFile(TEST_2247_STD_XLSX_FILE_NAME, {
14+
password: '123456',
15+
});
16+
const sheetName = workbook.getWorksheet(1).name;
17+
expect(sheetName).to.equal('Sheet1');
18+
});
19+
20+
it('workbook.xlsx.readFile, ecma376_agile encryption method decrypted successfully', async () => {
21+
const workbook = new ExcelJS.Workbook();
22+
await workbook.xlsx.readFile(TEST_2247_AGILE_XLSX_FILE_NAME, {
23+
password: '123456',
24+
});
25+
const sheetName = workbook.getWorksheet(1).name;
26+
expect(sheetName).to.equal('Sheet1');
27+
});
28+
29+
it('workbook.xlsx.load, ecma376_standard encryption method decrypted successfully ', async () => {
30+
const workbook = new ExcelJS.Workbook();
31+
32+
await workbook.xlsx.load(fs.readFileSync(TEST_2247_STD_XLSX_FILE_NAME), {
33+
password: '123456',
34+
});
35+
const sheetName = workbook.getWorksheet(1).name;
36+
expect(sheetName).to.equal('Sheet1');
37+
});
38+
39+
it('workbook.xlsx.load, ecma376_agile encryption method decrypted successfully ', async () => {
40+
const workbook = new ExcelJS.Workbook();
41+
42+
await workbook.xlsx.load(
43+
fs.readFileSync(TEST_2247_AGILE_XLSX_FILE_NAME),
44+
{
45+
password: '123456',
46+
}
47+
);
48+
const sheetName = workbook.getWorksheet(1).name;
49+
expect(sheetName).to.equal('Sheet1');
50+
});
51+
52+
it('workbook.xlsx.load, options.base64 = true, ecma376_standard encryption method decrypted successfully ', async () => {
53+
const workbook = new ExcelJS.Workbook();
54+
const input = fs
55+
.readFileSync(TEST_2247_STD_XLSX_FILE_NAME)
56+
.toString('base64');
57+
await workbook.xlsx.load(input, {
58+
password: '123456',
59+
base64: true,
60+
});
61+
const sheetName = workbook.getWorksheet(1).name;
62+
expect(sheetName).to.equal('Sheet1');
63+
});
64+
65+
it('workbook.xlsx.load, options.base64 = true, ecma376_agile encryption method decrypted successfully ', async () => {
66+
const workbook = new ExcelJS.Workbook();
67+
const input = fs
68+
.readFileSync(TEST_2247_AGILE_XLSX_FILE_NAME)
69+
.toString('base64');
70+
await workbook.xlsx.load(input, {
71+
password: '123456',
72+
base64: true,
73+
});
74+
const sheetName = workbook.getWorksheet(1).name;
75+
expect(sheetName).to.equal('Sheet1');
76+
});
77+
78+
it('workbook.xlsx.read, ecma376_standard encryption method decrypted successfully ', async () => {
79+
const workbook = new ExcelJS.Workbook();
80+
const input = fs.createReadStream(TEST_2247_STD_XLSX_FILE_NAME);
81+
await workbook.xlsx.read(input, {
82+
password: '123456',
83+
});
84+
const sheetName = workbook.getWorksheet(1).name;
85+
expect(sheetName).to.equal('Sheet1');
86+
});
87+
88+
it('workbook.xlsx.read, ecma376_agile encryption method decrypted successfully ', async () => {
89+
const workbook = new ExcelJS.Workbook();
90+
const input = fs.createReadStream(TEST_2247_AGILE_XLSX_FILE_NAME);
91+
await workbook.xlsx.read(input, {
92+
password: '123456',
93+
});
94+
const sheetName = workbook.getWorksheet(1).name;
95+
expect(sheetName).to.equal('Sheet1');
96+
});
97+
});
98+
});

spec/integration/issues/issue-2247-encrytor.spec.js

-20
This file was deleted.

0 commit comments

Comments
 (0)