在Node.js中解压密码保护的文件

18
有没有一个库可以用来解压带密码保护的文件(下载时网站要求我输入密码)?有很多库可以解压普通文件,但我找不到可以用密码解压的库。 这里我找到了一些有用的入门资料。但我不想使用child_process和内置的Unix解压功能,但这可能是我的最后选择。我甚至愿意对加密代码进行自己的操作,但我甚至无法找出如何确定加密类型(因为我可以在终端中执行它,所以它似乎只是非常标准的)。
再次强调,我不想这样做,但我担心这是我的唯一选择,因此我尝试了以下内容:
var fs = require('fs')
, unzip = require('unzip')
, spawn = require('child_process').spawn
, expand = function(filepath, cb) {
     var self = this
     , unzipStream = fs.createReadStream(filepath).pipe(unzip.Parse())
     , xmlData = '';

     unzipStream.on('entry', function (entry) {
            var filename = entry.path;
           // first zip contains files and one password protected zipfile. 
           // Here I can just do pipe(unzip.Parse()) again, but then i just get a giant encoded string that I don't know how to handle, so i tried other things bellow.
           if(filename.match(/\.zip$/)){
                 entry.on('data', function (data) {
                     var funzip = spawn('funzip','-P','ThisIsATestPsswd','-');
                     var newFile = funzip.stdin.write(data);

            // more closing code...

然后我有点不知道该怎么做了。我尝试将newFile写入文件,但只显示了[object]

接着我尝试了一些更简单的方法,将最后三行改为:

   fs.writeFile('./tmp/this.zip', data, 'binary');
   var newFile = spawn('unzip','-P','ThisIsATest','./tmp/this.zip');
   console.log('Data: ' + data);

但是数据仅仅是一个没有用处的[object Object]。我不知道接下来该怎么做,才能将这个新解压的文件变成一个可工作的文件或可读的字符串。

我对Node非常陌生,其他进程触发的异步/监听器有点让人困惑,所以如果有任何不清楚的地方,请见谅。非常感谢您的帮助!

编辑:


我现在已经添加了以下代码:

var fs = require('fs')
  , unzip = require('unzip')
  , spawn = require('child_process').spawn
  , expand = function(filepath, cb) {
    var self = this
    , unzipStream = fs.createReadStream(filepath)
      .pipe(unzip.Parse())
    , xmlData = '';

      unzipStream.on('entry', function (entry) {
        var filename = entry.path
            , type = entry.type // 'Directory' or 'File'
            , size = entry.size;
        console.log('Filename: ' + filename);

        if(filename.match(/\.zip$/)){
            entry.on('data', function (data) {
              fs.writeFile('./lib/mocks/tmp/this.zip', data, 'binary');
              var newFile = spawn('unzip','-P','ThisIsATestPassword', '-','../tmp/this.zip');
              newFile.stdout.on('data', function(data){
                 fs.writeFile('./lib/mocks/tmp/that.txt', data); //This needs to be something different
                    //The zip file contains an archive of files, so one file name shouldn't work
              });

           });
          } else { //Not a zip so handle differently }
       )};
    };

这似乎非常接近我所需要的,但当文件被写入时,它只有解压选项列表。
UnZip 5.52 of 28 February 2005, by Info-ZIP.  Maintained by C. Spieler.  Send
bug reports using http://www.info-zip.org/zip-bug.html; see README for details.

Usage: unzip [-Z] [-opts[modifiers]] file[.zip] [list] [-x xlist] [-d exdir]
  Default action is to extract files in list, except those in xlist, to exdir;
  file[.zip] may be a wildcard.  -Z => ZipInfo mode ("unzip -Z" for usage).

  -p  extract files to pipe, no messages     -l  list files (short format)
  -f  freshen existing files, create none    -t  test compressed archive data
  -u  update files, create if necessary      -z  display archive comment
  -x  exclude files that follow (in xlist)   -d  extract files into exdir

modifiers:                                   -q  quiet mode (-qq => quieter)
  -n  never overwrite existing files         -a  auto-convert any text files
  -o  overwrite files WITHOUT prompting      -aa treat ALL files as text
  -j  junk paths (do not make directories)   -v  be verbose/print version info
  -C  match filenames case-insensitively     -L  make (some) names lowercase
  -X  restore UID/GID info                   -V  retain VMS version numbers
  -K  keep setuid/setgid/tacky permissions   -M  pipe through "more" pager
Examples (see unzip.txt for more info):
  unzip data1 -x joe   => extract all files except joe from zipfile data1.zip
  unzip -p foo | more  => send contents of foo.zip via pipe into program more
  unzip -fo foo ReadMe => quietly replace existing ReadMe if archive file newer

我不确定输入是否有误,因为这看起来像是解压错误。或者我只是写错了内容。我本以为它会像通常从控制台执行一样 - 把所有文件添加到目录中。虽然我很想能够从缓冲区读取所有内容,但选项 - 并不似乎允许这样做,所以我将接受文件被添加到目录中。非常感谢任何提供建议的人!

我能够让它工作,可能不是最好的方式,但至少使用这行代码可以实现:

var newFile = spawn('unzip', [ '-P','ThisIsATestPassword', '-d','./lib/tmp/foo','./lib/mocks/tmp/this.zip' ])

这将只是把所有的文件解压到目录中,然后我就能够从那里读取它们了。我的错误在于第二个参数必须是一个数组。

使用 console.log('Data: ', data);。您正在连接字符串和对象,因此对象转换为字符串 [object Object] - Alexey Ten
哇,那帮了很多,谢谢! - CodySchaaf
当我使用这个日志记录功能时,我可以看到数据对象具有 _events: {data: function}。如何触发该数据事件?我尝试了 data.triggerEvent('data'),但是它只显示出未定义方法 triggerEvent。 - CodySchaaf
也许更好的问题是,在执行第二个选项后,我正在处理什么对象。它有一个stdin和一个stdout属性,这是否意味着它是一个readStream,我可以使用相同的方法来访问其中的数据? - CodySchaaf
4
你好,看起来你已经找到了正确的答案?如果是这样,我会把它作为一个回答发布出来。 - Lime
4个回答

9

我已经将其成功运行,可能不是最好的方法,但至少使用以下行代码可确保其正常工作:

var newFile = spawn('unzip', [ '-P','ThisIsATestPassword', '-d','./lib/tmp/foo','./lib/mocks/tmp/this.zip' ])

这将只是将所有文件解压到目录中,然后我可以从那里读取它们。我的错误在于第二个参数必须是一个数组。


5
我使用解压程序找到了解决方案。
这篇博客中复制了代码。
const unzipper = require('unzipper');

(async () => {
  try {
    const directory = await unzipper.Open.file('path/to/your.zip');
    const extracted = await directory.files[0].buffer('PASSWORD');
    console.log(extracted.toString()); // This will print the file content
  } catch(e) {
    console.log(e);
  }
})();


@codyschaaf在他的答案中提到的那样,我们可以使用spawn或其他child_process,但它们并不总是跨操作系统兼容的。因此,如果我在生产中使用它,我将始终选择跨操作系统兼容的解决方案(如果存在)。希望这能帮助到某些人。

对我来说,这个解决方案在 const extracted = await directory....... 处卡住了......没有错误,执行就停止了。 - codeKiller
很遗憾,这个解决方案不能用在AWS-256加密的文件上:/ 只适用于ZipCrypto。 - mch.zawalski

2
我尝试了 "spawn" 方法(实际上,"spawnSync" 的效果更好)。
const result = spawnSync('unzip', ['-P', 'password', '-d', './files', './files/file.zip'], { encoding: 'utf-8' })

然而,这种方法并没有完全奏效,因为它引入了一个新的错误:

Archive:  test.zip
   skipping: file.png                need PK compat. v5.1 (can do v4.6)

最终,我选择了7zip的方法:

import sevenBin from '7zip-bin'
import seven from 'node-7z'

const zipPath = './files/file.zip'
const downloadDirectory = './files'

const zipStream = seven.extractFull(zipPath, downloadDirectory, {
  password: 'password',
  $bin: sevenBin.path7za
})

zipStream.on('end', () => {
  // Do stuff with unzipped content
})

我正在尝试使用7zip方法,但是我遇到了以下错误。看起来它在PATH中找不到二进制文件? 错误:spawn 7za ENOENT at Process.ChildProcess._handle.onexit (internal/child_process.js:268:19) at onErrorNT (internal/child_process.js:470:16) at processTicksAndRejections (internal/process/task_queues.js:84:21) - freecks

0

如果要解压密码保护的多级文件夹,请尝试以下代码。我正在使用unzipper npm。

unzipper.Open.file(contentPath + filename).then((mainDirectory) => {
return new Promise((resolve, reject) => {
  let maindirPath = mainDirectory.files[0].path;
  let patharray = maindirPath.split("/")
  let temppath = destinationPath+patharray[0];
   fs.mkdirSync(temppath);//create parent Directory 
 
    // Iterate through every file inside there (this includes directories and files in subdirectories)
    for (let i = 0; i < mainDirectory.files.length; i++) {
        const file = mainDirectory.files[i];
        let filepath = Distinationpath + file.path
        
        if(file.path.endsWith("/")) {
            fs.mkdirSync(filepath);
        }
        else {
     
            file.stream(password).pipe(fs.createWriteStream(filepath))
                .on('finished', resolve)
                .on('error', reject);
        }
    }
});
});

这个不起作用,你还有一个"Distinationpath"和一个"destinationPath"。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接