Skip to content

Commit

Permalink
feat: adding support for filename encodings
Browse files Browse the repository at this point in the history
  • Loading branch information
martinheidegger committed Feb 12, 2021
1 parent d641571 commit 433f1f1
Show file tree
Hide file tree
Showing 9 changed files with 33 additions and 7 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ jobs:
- name: Lint
run: yarn lint
- name: Test
run: yarn coverage
run: yarn ci:coverage
10 changes: 8 additions & 2 deletions cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@ var extract = require('./')
var args = process.argv.slice(2)
var source = args[0]
var dest = args[1] || process.cwd()
var encodingReg = /^--encoding=*/
var encodingIndex = args.findIndex(entry => encodingReg.test(entry))
var encoding
if (encodingIndex !== -1) {
encoding = args.splice(encodingIndex, 1)[0].replace(encodingReg, '')
}
if (!source) {
console.error('Usage: extract-zip foo.zip <targetDirectory>')
console.error('Usage: extract-zip foo.zip <targetDirectory> [--encoding=<encoding>]')
process.exit(1)
}

extract(source, { dir: dest })
extract(source, { dir: dest, encoding })
.catch(function (err) {
console.error('error!', err)
process.exit(1)
Expand Down
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Entry, ZipFile } from 'yauzl';

declare namespace extract {
interface Options {
encoding?: string;
dir: string;
defaultDirMode?: number;
defaultFileMode?: number;
Expand Down
8 changes: 6 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const debug = require('debug')('extract-zip')
const { createWriteStream, promises: fs } = require('fs')
const getStream = require('get-stream')
const path = require('path')
const { promisify } = require('util')
const { promisify, TextDecoder } = require('util')
const stream = require('stream')
const yauzl = require('yauzl')

Expand All @@ -14,12 +14,13 @@ class Extractor {
constructor (zipPath, opts) {
this.zipPath = zipPath
this.opts = opts
this.decoder = new TextDecoder(opts.encoding)
}

async extract () {
debug('opening', this.zipPath, 'with opts', this.opts)

this.zipfile = await openZip(this.zipPath, { lazyEntries: true })
this.zipfile = await openZip(this.zipPath, { lazyEntries: true, decodeStrings: false })
this.canceled = false

return new Promise((resolve, reject) => {
Expand All @@ -37,6 +38,9 @@ class Extractor {
})

this.zipfile.on('entry', async entry => {
entry.fileName = this.decoder.decode(entry.fileName)
entry.fileNameLength = entry.fileName.length

/* istanbul ignore if */
if (this.canceled) {
debug('skipping entry', entry.fileName, { cancelled: this.canceled })
Expand Down
1 change: 1 addition & 0 deletions index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ let options: extract.Options = {
};
options = {
dir,
encoding: 'shift-jis',
defaultDirMode: 0o700,
defaultFileMode,
onEntry: (entry: Entry, zipfile: ZipFile): void => {
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"lint:js": "eslint .",
"lint:ts": "eslint --config .eslintrc.typescript.js --ext .ts .",
"test": "yarn lint && ava",
"ci:coverage": "full-icu node_modules/nyc/bin/nyc.js ava",
"tsd": "tsd"
},
"files": [
Expand All @@ -29,7 +30,7 @@
"extract"
],
"engines": {
"node": ">= 10.17.0"
"node": ">= 10.18.0"
},
"dependencies": {
"debug": "^4.1.1",
Expand All @@ -40,6 +41,7 @@
"@types/yauzl": "^2.9.1"
},
"devDependencies": {
"@leichtgewicht/full-icu": "^1.3.3",
"@typescript-eslint/eslint-plugin": "^3.2.0",
"@typescript-eslint/parser": "^3.2.0",
"ava": "^3.5.1",
Expand Down
5 changes: 4 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,19 @@ async function main () {
### Options

- `dir` (required) - the path to the directory where the extracted files are written
- `encoding` - string - [encoding][] to be used for file names, defaults to `utf-8`
- `defaultDirMode` - integer - Directory Mode (permissions), defaults to `0o755`
- `defaultFileMode` - integer - File Mode (permissions), defaults to `0o644`
- `onEntry` - function - if present, will be called with `(entry, zipfile)`, entry is every entry from the zip file forwarded from the `entry` event from yauzl. `zipfile` is the `yauzl` instance

Default modes are only used if no permissions are set in the zip file.

[encoding]: https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder/encoding

## CLI Usage

```
extract-zip foo.zip <targetDirectory>
extract-zip foo.zip <targetDirectory> [--encoding=<encoding>]
```

If not specified, `targetDirectory` will default to `process.cwd()`.
9 changes: 9 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const subdirZip = path.join(__dirname, 'file-in-subdir-without-subdir-entry.zip'
const symlinkDestZip = path.join(__dirname, 'symlink-dest.zip')
const symlinkZip = path.join(__dirname, 'symlink.zip')
const brokenZip = path.join(__dirname, 'broken.zip')
const mojibakeZip = path.join(__dirname, 'mojibake.zip')

const relativeTarget = './cats'

Expand Down Expand Up @@ -161,3 +162,11 @@ test('extract broken zip', async t => {
message: 'invalid central directory file header signature: 0x2014b00'
})
})

test('extract mojibake', async t => {
const dirPath = await mkdtemp(t, 'mojibake-zip')
await extract(mojibakeZip, { dir: dirPath, encoding: 'windows-949' })
await pathExists(t, path.join(dirPath, '새 텍스트 문서.txt'), 'file created')
await pathExists(t, path.join(dirPath, '새 폴더'), 'folder created')
await pathExists(t, path.join(dirPath, '새 폴더', '한글문서.txt'), 'subfile created')
})
Binary file added test/mojibake.zip
Binary file not shown.

0 comments on commit 433f1f1

Please sign in to comment.