node-unzipper icon indicating copy to clipboard operation
node-unzipper copied to clipboard

Unable to unzip a directory through a stream

Open aeisenberg opened this issue 5 years ago • 4 comments

Use case:

We unzip large zip files that can take a while to complete. We would like to attach a listener to the unzipping stream so that we can signal progress to the user.

When we call unzipper.Extract, the return value is a parseStream that we can attach listeners to. However, this fails when file metadata is not complete with respect to the central directory. So, we need to use something like:

const directory = await unzipper.Open.file(databaseFile);
await directory.extract({ path: unzipPath });

The problem is that extract does not return a stream (only a promise) and so we cannot signal progress. Is there some other way of achieving this? If not, is this a bug or a feature request?

aeisenberg avatar Dec 07 '20 23:12 aeisenberg

I think it depends on how you measure progress? If you are counting number of files extracted you can just iterate over the directory instead of using extract?

const directory = await unzipper.Open.file(databaseFile);
const archiveSize = directory.files.length;
directory.files.forEach(function(file, index) {
	file.stream().pipe(fs.createWriteStream(file.path));
	console.log(`Processed ${index+1} of ${archiveSize} files`);
});

You will need some error handling and concurrency control also.

warerebel avatar Jan 28 '21 11:01 warerebel

Would I also need to recursively traverse nested directories?

aeisenberg avatar Jan 28 '21 16:01 aeisenberg

No, the directory.files array will include all files in the archive. The file.path will include the full path of the file including subdirectories.

warerebel avatar Jan 29 '21 08:01 warerebel

Ah...I see. And actually your snippet above won't work either. The streaming will happen asynchronously and all of the log messages will be printed out immediately before the streaming even starts. It needs to be something like:

const fileStream = file.stream()
fileStream.pipe(fs.createWriteStream(file.path));
fileStream.on('end', () => console.log(...));

(Or something like that, I haven't tried it out, but I will.)

aeisenberg avatar Jan 29 '21 16:01 aeisenberg