w-zip 1.0.41 → 1.0.43

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.
@@ -29,7 +29,7 @@
29
29
  <nav >
30
30
 
31
31
 
32
- <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="m7z.html">m7z</a><ul class='methods'><li data-type='method'><a href="m7z.html#.setProg">setProg</a></li><li data-type='method'><a href="m7z.html#.unzip">unzip</a></li><li data-type='method'><a href="m7z.html#.zipFile">zipFile</a></li><li data-type='method'><a href="m7z.html#.zipFolder">zipFolder</a></li></ul></li><li><a href="mZip.html">mZip</a><ul class='methods'><li data-type='method'><a href="mZip.html#.unzip">unzip</a></li><li data-type='method'><a href="mZip.html#.zipFile">zipFile</a></li><li data-type='method'><a href="mZip.html#.zipFolder">zipFolder</a></li></ul></li></ul>
32
+ <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="m7z.html">m7z</a><ul class='methods'><li data-type='method'><a href="m7z.html#.setProg">setProg</a></li><li data-type='method'><a href="m7z.html#.unzip">unzip</a></li><li data-type='method'><a href="m7z.html#.zipFile">zipFile</a></li><li data-type='method'><a href="m7z.html#.zipFolder">zipFolder</a></li></ul></li><li><a href="mZip.html">mZip</a><ul class='methods'><li data-type='method'><a href="mZip.html#.listEntries">listEntries</a></li><li data-type='method'><a href="mZip.html#.readEntry">readEntry</a></li><li data-type='method'><a href="mZip.html#.unzip">unzip</a></li><li data-type='method'><a href="mZip.html#.zipFile">zipFile</a></li><li data-type='method'><a href="mZip.html#.zipFolder">zipFolder</a></li></ul></li></ul>
33
33
 
34
34
  </nav>
35
35
 
@@ -47,7 +47,7 @@
47
47
  <article>
48
48
  <pre class="prettyprint source linenums"><code>import path from 'path'
49
49
  import fs from 'fs'
50
- import { configure, ZipWriter, ZipReader, Uint8ArrayReader, Uint8ArrayWriter } from '@zip.js/zip.js'
50
+ import { configure, ZipWriter, ZipReader, Reader, Uint8ArrayReader, Uint8ArrayWriter, TextWriter } from '@zip.js/zip.js'
51
51
  import get from 'lodash-es/get.js'
52
52
  import fsTreeFolder from 'wsemi/src/fsTreeFolder.mjs'
53
53
  import getFileName from 'wsemi/src/getFileName.mjs'
@@ -62,7 +62,7 @@ configure({ useWebWorkers: false }) //關閉web worker改用主執行緒壓縮,
62
62
  * Zip處理
63
63
  *
64
64
  * @class
65
- * @returns {Object} 回傳壓縮物件,可使用函數zipFile、zipFolder、unzip
65
+ * @returns {Object} 回傳壓縮物件,可使用函數zipFile、zipFolder、unzip、listEntries、readEntry
66
66
  * @example
67
67
  * import wz from 'w-zip'
68
68
  *
@@ -109,6 +109,21 @@ configure({ useWebWorkers: false }) //關閉web worker改用主執行緒壓縮,
109
109
  * console.log('unzip2 with password', await wz.mZip.unzip(fpZip2PW, fpUnzipExtract + '/test2PW', { pw }))
110
110
  * console.log('unzip2 with password after')
111
111
  *
112
+ * //listEntries
113
+ * console.log('listEntries before')
114
+ * console.log('listEntries', (await wz.mZip.listEntries(fpZip2)).map((v) => v.filename))
115
+ * console.log('listEntries after')
116
+ *
117
+ * //readEntry
118
+ * console.log('readEntry before')
119
+ * console.log('readEntry', JSON.parse(await wz.mZip.readEntry(fpZip1, 'file1(中文).txt')).name)
120
+ * console.log('readEntry after')
121
+ *
122
+ * //readEntry with password
123
+ * console.log('readEntry with password before')
124
+ * console.log('readEntry with password', (await wz.mZip.readEntry(fpZip2PW, 'folder1/f1-1.xlsx', { type: 'u8', pw })).length)
125
+ * console.log('readEntry with password after')
126
+ *
112
127
  * }
113
128
  * test()
114
129
  * .catch((err) => {
@@ -133,6 +148,22 @@ configure({ useWebWorkers: false }) //關閉web worker改用主執行緒壓縮,
133
148
  * // unzip2 with password before
134
149
  * // unzip2 with password done: test2PW
135
150
  * // unzip2 with password after
151
+ * // listEntries before
152
+ * // listEntries [
153
+ * // 'folder1/f1-1.xlsx',
154
+ * // 'folder1/f1-2.xlsx',
155
+ * // 'folder1/folder2/',
156
+ * // 'folder1/folder2/f2-1.xlsx',
157
+ * // 'folder1/folder2/f2-2.xlsx',
158
+ * // 'folder1/folder2/f2-3(中文).xlsx'
159
+ * // ]
160
+ * // listEntries after
161
+ * // readEntry before
162
+ * // readEntry w-backup
163
+ * // readEntry after
164
+ * // readEntry with password before
165
+ * // readEntry with password 6663
166
+ * // readEntry with password after
136
167
  */
137
168
  function mZip() {
138
169
 
@@ -375,10 +406,219 @@ function mZip() {
375
406
  }
376
407
 
377
408
 
409
+ //checkSrc, 檢查不解壓讀取之來源, 可為檔案位置字串或Buffer、Uint8Array, 有誤回傳錯誤訊息字串, 無誤回傳空字串
410
+ function checkSrc(src) {
411
+
412
+ //Uint8Array, Buffer亦為Uint8Array
413
+ if (src instanceof Uint8Array) {
414
+ return ''
415
+ }
416
+
417
+ //check
418
+ if (typeof src !== 'string') {
419
+ return 'invalid src'
420
+ }
421
+ if (!fs.existsSync(src)) {
422
+ return 'invalid path of source file'
423
+ }
424
+ if (!fs.lstatSync(src).isFile()) {
425
+ return 'path of source is not file'
426
+ }
427
+
428
+ return ''
429
+ }
430
+
431
+
432
+ //readZip, 開啟src之ZipReader交由fun處理, 結束時(含失敗)關閉ZipReader與檔案代號
433
+ async function readZip(src, readerOpt, fun) {
434
+
435
+ //reader
436
+ let fh = null
437
+ let reader
438
+ if (src instanceof Uint8Array) {
439
+ reader = new Uint8ArrayReader(src)
440
+ }
441
+ else {
442
+
443
+ //reader, 以zip.js之Reader為基底(沿用其createReadable)補上init與readUint8Array, 依位置隨機讀取檔案, 只讀中央目錄與所需項目而不載入整檔
444
+ reader = new Reader()
445
+ reader.init = async () => {
446
+ fh = await fs.promises.open(src, 'r')
447
+ reader.size = (await fh.stat()).size
448
+ reader.initialized = true
449
+ }
450
+ reader.readUint8Array = async (index, length) => {
451
+
452
+ //len, 依檔案實際大小夾限, 避免依異常之宣告長度配置過大緩衝區(同Uint8ArrayReader以slice夾限)
453
+ let len = Math.max(0, Math.min(length, reader.size - index))
454
+
455
+ //read, 迴圈讀滿以處理短讀, 讀到0位元組視為檔尾
456
+ let u8 = new Uint8Array(len)
457
+ let n = 0
458
+ while (n &lt; len) {
459
+ let { bytesRead } = await fh.read(u8, n, len - n, index + n)
460
+ if (bytesRead === 0) {
461
+ break
462
+ }
463
+ n += bytesRead
464
+ }
465
+
466
+ return n &lt; len ? u8.subarray(0, n) : u8
467
+ }
468
+
469
+ }
470
+
471
+ //zipReader, 項目名之'\'視為路徑分隔而正規化為'/'(壓縮檔規範要求以'/'分隔, 部分工具寫入'\')
472
+ let zipReader = new ZipReader(reader, Object.assign({}, readerOpt, {
473
+ normalizeFilename: (filename) => filename.replaceAll('\\', '/'),
474
+ }))
475
+
476
+ try {
477
+ return await fun(zipReader)
478
+ }
479
+ finally {
480
+ try {
481
+ await zipReader.close()
482
+ }
483
+ finally {
484
+ if (fh !== null) {
485
+ await fh.close()
486
+ }
487
+ }
488
+ }
489
+ }
490
+
491
+
492
+ /**
493
+ * 列出壓縮檔內項目,不解壓至資料夾
494
+ *
495
+ * 加密之壓縮檔不需密碼即可列出。含不安全路徑(例如'../'、絕對路徑、磁碟代號)項目之壓縮檔會整個reject。
496
+ * 項目名以'\'分隔者會正規化為'/'。項目名未標示UTF-8且無Unicode路徑欄位者(例如部分工具以Big5寫入),依zip.js預設以CP437解碼,名稱會呈現亂碼,讀取時須使用回傳之名稱。
497
+ *
498
+ * @memberof mZip
499
+ * @param {String|Buffer|Uint8Array} src 輸入壓縮檔案位置字串,或壓縮檔內容之Buffer、Uint8Array,給予檔案位置時只讀取中央目錄而不載入整個檔案
500
+ * @returns {Promise} 回傳Promise,resolve為項目陣列,依壓縮檔中央目錄順序,各項目為{filename,directory,uncompressedSize,compressedSize,encrypted,lastModDate},filename為壓縮檔內以'/'分隔之路徑,reject為失敗資訊
501
+ */
502
+ async function listEntries(src) {
503
+
504
+ //check
505
+ let errSrc = checkSrc(src)
506
+ if (errSrc !== '') {
507
+ return Promise.reject(errSrc)
508
+ }
509
+
510
+ try {
511
+
512
+ //entries, 轉為純物件, 不外露zip.js之entry(讀取器關閉後其getData已不可用)
513
+ let entries = await readZip(src, {}, async (zipReader) => {
514
+ let es = await zipReader.getEntries()
515
+ return es.map((entry) => {
516
+ return {
517
+ filename: entry.filename,
518
+ directory: entry.directory,
519
+ uncompressedSize: entry.uncompressedSize,
520
+ compressedSize: entry.compressedSize,
521
+ encrypted: entry.encrypted,
522
+ lastModDate: entry.lastModDate,
523
+ }
524
+ })
525
+ })
526
+
527
+ return entries
528
+ }
529
+ catch (err) {
530
+ return Promise.reject(err)
531
+ }
532
+ }
533
+
534
+
535
+ /**
536
+ * 讀取壓縮檔內單一項目,不解壓至資料夾
537
+ *
538
+ * 讀出內容會驗證CRC32,資料損毀或密碼錯誤時reject。含不安全路徑(例如'../'、絕對路徑、磁碟代號)項目之壓縮檔會整個reject。
539
+ *
540
+ * @memberof mZip
541
+ * @param {String|Buffer|Uint8Array} src 輸入壓縮檔案位置字串,或壓縮檔內容之Buffer、Uint8Array,給予檔案位置時只讀取中央目錄與該項目而不載入整個檔案
542
+ * @param {String} filename 輸入項目路徑字串,須與listEntries回傳之filename完全相同(以'/'分隔且區分大小寫),若有同名項目則取最後一筆
543
+ * @param {Object} [opt={}] 輸入設定物件,預設{}
544
+ * @param {String} [opt.type='text'] 輸入回傳資料型態字串,可選'text'為UTF-8解碼之字串(去除BOM)、'u8'為Uint8Array,非UTF-8之文字內容請用'u8'自行解碼,預設'text'
545
+ * @param {String} [opt.pw=''] 輸入解壓縮密碼字串,預設''
546
+ * @returns {Promise} 回傳Promise,resolve為項目內容之字串或Uint8Array,reject為失敗資訊,找不到項目或項目為資料夾時亦為reject
547
+ */
548
+ async function readEntry(src, filename, opt = {}) {
549
+
550
+ //check
551
+ let errSrc = checkSrc(src)
552
+ if (errSrc !== '') {
553
+ return Promise.reject(errSrc)
554
+ }
555
+ if (typeof filename !== 'string' || filename === '') {
556
+ return Promise.reject('invalid filename')
557
+ }
558
+
559
+ //default
560
+ let type = get(opt, 'type', 'text')
561
+ let pw = get(opt, 'pw', '')
562
+
563
+ //check type
564
+ if (type !== 'text' &amp;&amp; type !== 'u8') {
565
+ return Promise.reject('invalid opt.type')
566
+ }
567
+
568
+ //check pw
569
+ if (typeof pw !== 'string') {
570
+ return Promise.reject('invalid opt.pw')
571
+ }
572
+
573
+ try {
574
+
575
+ //readerOpt, 驗證CRC32以免資料損毀或錯誤密碼(ZipCrypto僅以1位元組驗證密碼)時回傳錯誤內容
576
+ let readerOpt = { checkCrc32: true }
577
+ if (pw !== '') {
578
+ readerOpt.password = pw
579
+ }
580
+
581
+ //data
582
+ let data = await readZip(src, readerOpt, async (zipReader) => {
583
+
584
+ //entry, 同名取最後一筆, 同JSZip、node-stream-zip、Python zipfile之慣例
585
+ let entry = null
586
+ let entries = await zipReader.getEntries()
587
+ for (let e of entries) {
588
+ if (e.filename === filename) {
589
+ entry = e
590
+ }
591
+ }
592
+
593
+ //check
594
+ if (entry === null) {
595
+ return Promise.reject(`entry not found: ${filename}`)
596
+ }
597
+ if (entry.directory) {
598
+ return Promise.reject(`entry is directory: ${filename}`)
599
+ }
600
+
601
+ //getData, text同zip.js之TextWriter(UTF-8且去除BOM)
602
+ if (type === 'u8') {
603
+ return entry.getData(new Uint8ArrayWriter())
604
+ }
605
+ return entry.getData(new TextWriter())
606
+ })
607
+
608
+ return data
609
+ }
610
+ catch (err) {
611
+ return Promise.reject(err)
612
+ }
613
+ }
614
+
615
+
378
616
  return {
379
617
  zipFile,
380
618
  zipFolder,
381
619
  unzip,
620
+ listEntries,
621
+ readEntry,
382
622
  }
383
623
  }
384
624
 
@@ -398,7 +638,7 @@ export default mZip()
398
638
  <br class="clear">
399
639
 
400
640
  <footer>
401
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Tue Sep 22 2026 18:34:49 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
641
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Fri Oct 02 2026 15:37:30 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
402
642
  </footer>
403
643
 
404
644
  <script>prettyPrint();</script>
@@ -56,4 +56,4 @@ test()
56
56
  console.log(err)
57
57
  })
58
58
 
59
- //node g-7z.mjs
59
+ //node g_7z.mjs
@@ -45,6 +45,18 @@ async function test() {
45
45
  console.log('unzip2 with password')
46
46
  console.log(await wz.mZip.unzip(fpZip2PW, fpUnzip2PW, { pw }))
47
47
 
48
+ //listEntries
49
+ console.log('listEntries')
50
+ console.log((await wz.mZip.listEntries(fpZip2)).map((v) => v.filename))
51
+
52
+ //readEntry
53
+ console.log('readEntry')
54
+ console.log(JSON.parse(await wz.mZip.readEntry(fpZip1, 'file1(中文).txt')).name)
55
+
56
+ //readEntry with password
57
+ console.log('readEntry with password')
58
+ console.log((await wz.mZip.readEntry(fpZip2PW, 'folder1/f1-1.xlsx', { type: 'u8', pw })).length)
59
+
48
60
  console.log('finish')
49
61
  }
50
62
  test()
@@ -52,4 +64,4 @@ test()
52
64
  console.log(err)
53
65
  })
54
66
 
55
- //node g-zip.mjs
67
+ //node g_zip.mjs
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "w-zip",
3
- "version": "1.0.41",
3
+ "version": "1.0.43",
4
4
  "main": "dist/w-zip.umd.js",
5
5
  "dependencies": {
6
- "@zip.js/zip.js": "^2.8.26",
7
- "wsemi": "^1.9.3"
6
+ "@zip.js/zip.js": "^2.22.0",
7
+ "wsemi": "^1.9.9"
8
8
  },
9
9
  "devDependencies": {
10
10
  "w-package-tools": "^1.1.16"
package/src/mZip.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import path from 'path'
2
2
  import fs from 'fs'
3
- import { configure, ZipWriter, ZipReader, Uint8ArrayReader, Uint8ArrayWriter } from '@zip.js/zip.js'
3
+ import { configure, ZipWriter, ZipReader, Reader, Uint8ArrayReader, Uint8ArrayWriter, TextWriter } from '@zip.js/zip.js'
4
4
  import get from 'lodash-es/get.js'
5
5
  import fsTreeFolder from 'wsemi/src/fsTreeFolder.mjs'
6
6
  import getFileName from 'wsemi/src/getFileName.mjs'
@@ -15,7 +15,7 @@ configure({ useWebWorkers: false }) //關閉web worker改用主執行緒壓縮,
15
15
  * Zip處理
16
16
  *
17
17
  * @class
18
- * @returns {Object} 回傳壓縮物件,可使用函數zipFile、zipFolder、unzip
18
+ * @returns {Object} 回傳壓縮物件,可使用函數zipFile、zipFolder、unzip、listEntries、readEntry
19
19
  * @example
20
20
  * import wz from 'w-zip'
21
21
  *
@@ -62,6 +62,21 @@ configure({ useWebWorkers: false }) //關閉web worker改用主執行緒壓縮,
62
62
  * console.log('unzip2 with password', await wz.mZip.unzip(fpZip2PW, fpUnzipExtract + '/test2PW', { pw }))
63
63
  * console.log('unzip2 with password after')
64
64
  *
65
+ * //listEntries
66
+ * console.log('listEntries before')
67
+ * console.log('listEntries', (await wz.mZip.listEntries(fpZip2)).map((v) => v.filename))
68
+ * console.log('listEntries after')
69
+ *
70
+ * //readEntry
71
+ * console.log('readEntry before')
72
+ * console.log('readEntry', JSON.parse(await wz.mZip.readEntry(fpZip1, 'file1(中文).txt')).name)
73
+ * console.log('readEntry after')
74
+ *
75
+ * //readEntry with password
76
+ * console.log('readEntry with password before')
77
+ * console.log('readEntry with password', (await wz.mZip.readEntry(fpZip2PW, 'folder1/f1-1.xlsx', { type: 'u8', pw })).length)
78
+ * console.log('readEntry with password after')
79
+ *
65
80
  * }
66
81
  * test()
67
82
  * .catch((err) => {
@@ -86,6 +101,22 @@ configure({ useWebWorkers: false }) //關閉web worker改用主執行緒壓縮,
86
101
  * // unzip2 with password before
87
102
  * // unzip2 with password done: test2PW
88
103
  * // unzip2 with password after
104
+ * // listEntries before
105
+ * // listEntries [
106
+ * // 'folder1/f1-1.xlsx',
107
+ * // 'folder1/f1-2.xlsx',
108
+ * // 'folder1/folder2/',
109
+ * // 'folder1/folder2/f2-1.xlsx',
110
+ * // 'folder1/folder2/f2-2.xlsx',
111
+ * // 'folder1/folder2/f2-3(中文).xlsx'
112
+ * // ]
113
+ * // listEntries after
114
+ * // readEntry before
115
+ * // readEntry w-backup
116
+ * // readEntry after
117
+ * // readEntry with password before
118
+ * // readEntry with password 6663
119
+ * // readEntry with password after
89
120
  */
90
121
  function mZip() {
91
122
 
@@ -328,10 +359,219 @@ function mZip() {
328
359
  }
329
360
 
330
361
 
362
+ //checkSrc, 檢查不解壓讀取之來源, 可為檔案位置字串或Buffer、Uint8Array, 有誤回傳錯誤訊息字串, 無誤回傳空字串
363
+ function checkSrc(src) {
364
+
365
+ //Uint8Array, Buffer亦為Uint8Array
366
+ if (src instanceof Uint8Array) {
367
+ return ''
368
+ }
369
+
370
+ //check
371
+ if (typeof src !== 'string') {
372
+ return 'invalid src'
373
+ }
374
+ if (!fs.existsSync(src)) {
375
+ return 'invalid path of source file'
376
+ }
377
+ if (!fs.lstatSync(src).isFile()) {
378
+ return 'path of source is not file'
379
+ }
380
+
381
+ return ''
382
+ }
383
+
384
+
385
+ //readZip, 開啟src之ZipReader交由fun處理, 結束時(含失敗)關閉ZipReader與檔案代號
386
+ async function readZip(src, readerOpt, fun) {
387
+
388
+ //reader
389
+ let fh = null
390
+ let reader
391
+ if (src instanceof Uint8Array) {
392
+ reader = new Uint8ArrayReader(src)
393
+ }
394
+ else {
395
+
396
+ //reader, 以zip.js之Reader為基底(沿用其createReadable)補上init與readUint8Array, 依位置隨機讀取檔案, 只讀中央目錄與所需項目而不載入整檔
397
+ reader = new Reader()
398
+ reader.init = async () => {
399
+ fh = await fs.promises.open(src, 'r')
400
+ reader.size = (await fh.stat()).size
401
+ reader.initialized = true
402
+ }
403
+ reader.readUint8Array = async (index, length) => {
404
+
405
+ //len, 依檔案實際大小夾限, 避免依異常之宣告長度配置過大緩衝區(同Uint8ArrayReader以slice夾限)
406
+ let len = Math.max(0, Math.min(length, reader.size - index))
407
+
408
+ //read, 迴圈讀滿以處理短讀, 讀到0位元組視為檔尾
409
+ let u8 = new Uint8Array(len)
410
+ let n = 0
411
+ while (n < len) {
412
+ let { bytesRead } = await fh.read(u8, n, len - n, index + n)
413
+ if (bytesRead === 0) {
414
+ break
415
+ }
416
+ n += bytesRead
417
+ }
418
+
419
+ return n < len ? u8.subarray(0, n) : u8
420
+ }
421
+
422
+ }
423
+
424
+ //zipReader, 項目名之'\'視為路徑分隔而正規化為'/'(壓縮檔規範要求以'/'分隔, 部分工具寫入'\')
425
+ let zipReader = new ZipReader(reader, Object.assign({}, readerOpt, {
426
+ normalizeFilename: (filename) => filename.replaceAll('\\', '/'),
427
+ }))
428
+
429
+ try {
430
+ return await fun(zipReader)
431
+ }
432
+ finally {
433
+ try {
434
+ await zipReader.close()
435
+ }
436
+ finally {
437
+ if (fh !== null) {
438
+ await fh.close()
439
+ }
440
+ }
441
+ }
442
+ }
443
+
444
+
445
+ /**
446
+ * 列出壓縮檔內項目,不解壓至資料夾
447
+ *
448
+ * 加密之壓縮檔不需密碼即可列出。含不安全路徑(例如'../'、絕對路徑、磁碟代號)項目之壓縮檔會整個reject。
449
+ * 項目名以'\'分隔者會正規化為'/'。項目名未標示UTF-8且無Unicode路徑欄位者(例如部分工具以Big5寫入),依zip.js預設以CP437解碼,名稱會呈現亂碼,讀取時須使用回傳之名稱。
450
+ *
451
+ * @memberof mZip
452
+ * @param {String|Buffer|Uint8Array} src 輸入壓縮檔案位置字串,或壓縮檔內容之Buffer、Uint8Array,給予檔案位置時只讀取中央目錄而不載入整個檔案
453
+ * @returns {Promise} 回傳Promise,resolve為項目陣列,依壓縮檔中央目錄順序,各項目為{filename,directory,uncompressedSize,compressedSize,encrypted,lastModDate},filename為壓縮檔內以'/'分隔之路徑,reject為失敗資訊
454
+ */
455
+ async function listEntries(src) {
456
+
457
+ //check
458
+ let errSrc = checkSrc(src)
459
+ if (errSrc !== '') {
460
+ return Promise.reject(errSrc)
461
+ }
462
+
463
+ try {
464
+
465
+ //entries, 轉為純物件, 不外露zip.js之entry(讀取器關閉後其getData已不可用)
466
+ let entries = await readZip(src, {}, async (zipReader) => {
467
+ let es = await zipReader.getEntries()
468
+ return es.map((entry) => {
469
+ return {
470
+ filename: entry.filename,
471
+ directory: entry.directory,
472
+ uncompressedSize: entry.uncompressedSize,
473
+ compressedSize: entry.compressedSize,
474
+ encrypted: entry.encrypted,
475
+ lastModDate: entry.lastModDate,
476
+ }
477
+ })
478
+ })
479
+
480
+ return entries
481
+ }
482
+ catch (err) {
483
+ return Promise.reject(err)
484
+ }
485
+ }
486
+
487
+
488
+ /**
489
+ * 讀取壓縮檔內單一項目,不解壓至資料夾
490
+ *
491
+ * 讀出內容會驗證CRC32,資料損毀或密碼錯誤時reject。含不安全路徑(例如'../'、絕對路徑、磁碟代號)項目之壓縮檔會整個reject。
492
+ *
493
+ * @memberof mZip
494
+ * @param {String|Buffer|Uint8Array} src 輸入壓縮檔案位置字串,或壓縮檔內容之Buffer、Uint8Array,給予檔案位置時只讀取中央目錄與該項目而不載入整個檔案
495
+ * @param {String} filename 輸入項目路徑字串,須與listEntries回傳之filename完全相同(以'/'分隔且區分大小寫),若有同名項目則取最後一筆
496
+ * @param {Object} [opt={}] 輸入設定物件,預設{}
497
+ * @param {String} [opt.type='text'] 輸入回傳資料型態字串,可選'text'為UTF-8解碼之字串(去除BOM)、'u8'為Uint8Array,非UTF-8之文字內容請用'u8'自行解碼,預設'text'
498
+ * @param {String} [opt.pw=''] 輸入解壓縮密碼字串,預設''
499
+ * @returns {Promise} 回傳Promise,resolve為項目內容之字串或Uint8Array,reject為失敗資訊,找不到項目或項目為資料夾時亦為reject
500
+ */
501
+ async function readEntry(src, filename, opt = {}) {
502
+
503
+ //check
504
+ let errSrc = checkSrc(src)
505
+ if (errSrc !== '') {
506
+ return Promise.reject(errSrc)
507
+ }
508
+ if (typeof filename !== 'string' || filename === '') {
509
+ return Promise.reject('invalid filename')
510
+ }
511
+
512
+ //default
513
+ let type = get(opt, 'type', 'text')
514
+ let pw = get(opt, 'pw', '')
515
+
516
+ //check type
517
+ if (type !== 'text' && type !== 'u8') {
518
+ return Promise.reject('invalid opt.type')
519
+ }
520
+
521
+ //check pw
522
+ if (typeof pw !== 'string') {
523
+ return Promise.reject('invalid opt.pw')
524
+ }
525
+
526
+ try {
527
+
528
+ //readerOpt, 驗證CRC32以免資料損毀或錯誤密碼(ZipCrypto僅以1位元組驗證密碼)時回傳錯誤內容
529
+ let readerOpt = { checkCrc32: true }
530
+ if (pw !== '') {
531
+ readerOpt.password = pw
532
+ }
533
+
534
+ //data
535
+ let data = await readZip(src, readerOpt, async (zipReader) => {
536
+
537
+ //entry, 同名取最後一筆, 同JSZip、node-stream-zip、Python zipfile之慣例
538
+ let entry = null
539
+ let entries = await zipReader.getEntries()
540
+ for (let e of entries) {
541
+ if (e.filename === filename) {
542
+ entry = e
543
+ }
544
+ }
545
+
546
+ //check
547
+ if (entry === null) {
548
+ return Promise.reject(`entry not found: ${filename}`)
549
+ }
550
+ if (entry.directory) {
551
+ return Promise.reject(`entry is directory: ${filename}`)
552
+ }
553
+
554
+ //getData, text同zip.js之TextWriter(UTF-8且去除BOM)
555
+ if (type === 'u8') {
556
+ return entry.getData(new Uint8ArrayWriter())
557
+ }
558
+ return entry.getData(new TextWriter())
559
+ })
560
+
561
+ return data
562
+ }
563
+ catch (err) {
564
+ return Promise.reject(err)
565
+ }
566
+ }
567
+
568
+
331
569
  return {
332
570
  zipFile,
333
571
  zipFolder,
334
572
  unzip,
573
+ listEntries,
574
+ readEntry,
335
575
  }
336
576
  }
337
577