-
Notifications
You must be signed in to change notification settings - Fork 34
feat: support xz #112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
feat: support xz #112
Changes from 3 commits
b104f8f
58055ea
9b17e98
d80454e
ee421a6
e8465b5
4f0f447
8d9380f
b167355
b3cb500
d6a07f0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -199,3 +199,25 @@ export namespace zip { | |||||
} | ||||||
|
||||||
} | ||||||
|
||||||
export namespace xz { | ||||||
function compressFile(source: sourceType, dest: destType, opts?: any): Promise<void> | ||||||
function uncompress(source: sourceType, dest: destType, opts?: any): Promise<void> | ||||||
function decompress(source: sourceType, dest: destType, opts?: any): Promise<void> | ||||||
|
||||||
export class FileStream extends ReadStream { | ||||||
constructor(opts?: { | ||||||
lzma?: object, | ||||||
source: sourceType | ||||||
}); | ||||||
} | ||||||
|
||||||
export class UncompressStream extends WriteStream { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
|
||||||
constructor(opts?: { | ||||||
lzma?: object, | ||||||
source: sourceType | ||||||
}); | ||||||
on(event: string, listener: (...args: any[]) => void): this | ||||||
on(event: 'error', listener: (err: Error) => void): this | ||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
'use strict'; | ||
|
||
const fs = require('fs'); | ||
const lzma = require('lzma-native'); | ||
const utils = require('../utils'); | ||
const streamifier = require('streamifier'); | ||
const stream = require('stream'); | ||
|
||
class XzFileStream extends stream.Transform { | ||
constructor(opts) { | ||
opts = opts || {}; | ||
super(opts); | ||
|
||
const sourceType = utils.sourceType(opts.source); | ||
const compressor = lzma.createCompressor(opts.lzma); | ||
|
||
compressor.on('error', err => this.emit('error', err)); | ||
compressor.on('end', () => this.push(null)); | ||
compressor.on('data', chunk => this.push(chunk)); | ||
|
||
if (sourceType === 'file') { | ||
const stream = fs.createReadStream(opts.source, opts.fs); | ||
stream.on('error', err => this.emit('error', err)); | ||
stream.pipe(compressor); | ||
return; | ||
} | ||
|
||
if (sourceType === 'buffer') { | ||
const stream = streamifier.createReadStream(opts.source, opts.streamifier); | ||
stream.on('error', err => this.emit('error', err)); | ||
stream.pipe(compressor); | ||
return; | ||
} | ||
|
||
if (sourceType === 'stream') { | ||
opts.source.on('error', err => this.emit('error', err)); | ||
opts.source.pipe(compressor); | ||
return; | ||
} | ||
|
||
// For streaming input | ||
this.on('pipe', srcStream => { | ||
srcStream.unpipe(srcStream); | ||
fengmk2 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
srcStream.pipe(compressor); | ||
}); | ||
} | ||
|
||
_transform(chunk, encoding, callback) { | ||
// This will be handled by the compressor stream | ||
callback(); | ||
} | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
module.exports = XzFileStream; |
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,11 @@ | ||||||||||||||
'use strict'; | ||||||||||||||
|
||||||||||||||
const utils = require('../utils'); | ||||||||||||||
const XzFileStream = require('./file_stream'); | ||||||||||||||
const XzUncompressStream = require('./uncompress_stream'); | ||||||||||||||
|
||||||||||||||
exports.FileStream = XzFileStream; | ||||||||||||||
exports.UncompressStream = XzUncompressStream; | ||||||||||||||
exports.compressFile = utils.makeFileProcessFn(XzFileStream); | ||||||||||||||
exports.uncompress = utils.makeFileProcessFn(XzUncompressStream); | ||||||||||||||
|
exports.compressFile = utils.makeFileProcessFn(XzFileStream); | |
exports.uncompress = utils.makeFileProcessFn(XzUncompressStream); | |
exports.compressFile = utils.makeFileProcessFn(XzFileStream); | |
// Alias for decompressing files. Provided for consistency with other APIs that use "uncompress". | |
exports.uncompress = utils.makeFileProcessFn(XzUncompressStream); | |
// Alias for decompressing files. Provided for developer preference and readability. |
Copilot uses AI. Check for mistakes.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
'use strict'; | ||
|
||
const fs = require('fs'); | ||
const lzma = require('lzma-native'); | ||
const utils = require('../utils'); | ||
const streamifier = require('streamifier'); | ||
const { PassThrough } = require('stream'); | ||
|
||
class XzUncompressStream extends PassThrough { | ||
constructor(opts) { | ||
opts = opts || {}; | ||
super(opts); | ||
|
||
const sourceType = utils.sourceType(opts.source); | ||
const decompressor = lzma.createDecompressor(opts.lzma); | ||
|
||
decompressor.on('error', err => this.emit('error', err)); | ||
decompressor.on('end', () => this.end()); | ||
|
||
// Handle single file decompression | ||
if (sourceType === 'file') { | ||
const stream = fs.createReadStream(opts.source, opts.fs); | ||
stream.on('error', err => this.emit('error', err)); | ||
stream.pipe(decompressor).pipe(this); | ||
return; | ||
} | ||
|
||
if (sourceType === 'buffer') { | ||
const stream = streamifier.createReadStream(opts.source, opts.streamifier); | ||
stream.on('error', err => this.emit('error', err)); | ||
stream.pipe(decompressor).pipe(this); | ||
return; | ||
} | ||
|
||
if (sourceType === 'stream') { | ||
opts.source.on('error', err => this.emit('error', err)); | ||
opts.source.pipe(decompressor).pipe(this); | ||
return; | ||
} | ||
|
||
// For streaming input | ||
this.on('pipe', srcStream => { | ||
srcStream.unpipe(this); | ||
srcStream.pipe(decompressor).pipe(this); | ||
}); | ||
Comment on lines
+46
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to |
||
} | ||
} | ||
} | ||
coderabbitai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
module.exports = XzUncompressStream; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -42,6 +42,7 @@ | |
"flushwritable": "^1.0.0", | ||
"get-ready": "^1.0.0", | ||
"iconv-lite": "^0.5.0", | ||
"lzma-native": "^8.0.5", | ||
|
||
"mkdirp": "^0.5.1", | ||
"pump": "^3.0.0", | ||
"streamifier": "^0.1.1", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const assert = require('assert'); | ||
const compressing = require('../..'); | ||
|
||
describe('test/xz/file_stream.test.js', () => { | ||
const sourceFile = path.join(__dirname, '../fixtures/xx.log'); | ||
const xzFile = path.join(__dirname, '../fixtures/xx.log.xz'); | ||
|
||
it('should compress file to xz', done => { | ||
const dest = path.join(__dirname, '../fixtures/xx.log.xz.tmp'); | ||
compressing.xz.compressFile(sourceFile, dest) | ||
.then(() => { | ||
assert(fs.existsSync(dest)); | ||
// 文件大小应该小于原始文件 | ||
assert(fs.statSync(dest).size < fs.statSync(sourceFile).size); | ||
fs.unlinkSync(dest); | ||
done(); | ||
}) | ||
.catch(done); | ||
}); | ||
|
||
it('should decompress xz file to log', done => { | ||
const dest = path.join(__dirname, '../fixtures/xx.log.tmp'); | ||
compressing.xz.uncompress(xzFile, dest) | ||
.then(() => { | ||
assert(fs.existsSync(dest)); | ||
// 内容应该一致 | ||
const raw = fs.readFileSync(sourceFile); | ||
const out = fs.readFileSync(dest); | ||
assert(raw.equals(out)); | ||
fs.unlinkSync(dest); | ||
done(); | ||
}) | ||
.catch(done); | ||
}); | ||
|
||
it('should compress buffer to xz', done => { | ||
const buf = fs.readFileSync(sourceFile); | ||
const dest = path.join(__dirname, '../fixtures/xx.log.xz.tmp'); | ||
compressing.xz.compressFile(buf, dest) | ||
.then(() => { | ||
assert(fs.existsSync(dest)); | ||
fs.unlinkSync(dest); | ||
done(); | ||
}) | ||
.catch(done); | ||
}); | ||
|
||
it('should decompress xz buffer to log', done => { | ||
const buf = fs.readFileSync(xzFile); | ||
const dest = path.join(__dirname, '../fixtures/xx.log.tmp'); | ||
compressing.xz.uncompress(buf, dest) | ||
.then(() => { | ||
assert(fs.existsSync(dest)); | ||
const raw = fs.readFileSync(sourceFile); | ||
const out = fs.readFileSync(dest); | ||
assert(raw.equals(out)); | ||
fs.unlinkSync(dest); | ||
done(); | ||
}) | ||
.catch(done); | ||
}); | ||
|
||
it('should compress stream to xz', done => { | ||
const src = fs.createReadStream(sourceFile); | ||
const dest = path.join(__dirname, '../fixtures/xx.log.xz.tmp'); | ||
compressing.xz.compressFile(src, dest) | ||
.then(() => { | ||
assert(fs.existsSync(dest)); | ||
fs.unlinkSync(dest); | ||
done(); | ||
}) | ||
.catch(done); | ||
}); | ||
|
||
it('should decompress xz stream to log', done => { | ||
const src = fs.createReadStream(xzFile); | ||
const dest = path.join(__dirname, '../fixtures/xx.log.tmp'); | ||
compressing.xz.uncompress(src, dest) | ||
.then(() => { | ||
assert(fs.existsSync(dest)); | ||
const raw = fs.readFileSync(sourceFile); | ||
const out = fs.readFileSync(dest); | ||
assert(raw.equals(out)); | ||
fs.unlinkSync(dest); | ||
done(); | ||
}) | ||
.catch(done); | ||
}); | ||
}); |
Uh oh!
There was an error while loading. Please reload this page.