Nodejs使用archiver-zip-encrypted库加密压缩文件时报错(解决方案)


Posted in NodeJs onNovember 18, 2019

前几天在维护一个nodejs写的命令行工具,要增加一个压缩zip文件时加密码功能。压缩文件时使用了 archiver 库,加密码使用了 archiver-zip-encrypted 库。在windows系统上测试时,发现会概率的出现以下异常:

events.js:174 
throw er; // Unhandled 'error' event 
^
Error: file data stream has unexpected number of bytes 
at ByteCounter. (
xxx\node_modules\yazl\index.js:162:99) 
at ByteCounter.emit (events.js:194:15) 
at endReadableNT (_stream_readable.js:1103:12) 
at process._tickCallback (internal/process/next_tick.js:63:19) 
Emitted 'error' event at: 
at ByteCounter. (xxx\node_modules\yazl\index.js:162:85) 
at ByteCounter.emit (events.js:194:15) 
at endReadableNT (_stream_readable.js:1103:12) 
at process._tickCallback (internal/process/next_t

我的本机环境是:

npm:6.9.0
node: v10.16.3

在另外一个同事的windows系统上测试,他那边是上面异常必现,对应的node版本是v10.15。

具体使用的代码不贴了,基本上是参照官方 demo 来写的,压缩完成最后调用代码如下所示:

archive.finalize().then(() => {
  // 到这里认为是压缩完成,进行后续处理,实际并没有,参照后面分析
  anotherProcess();
}).catch(err => {
  // 压缩出现异常处理...
});

出现异常后一一检查代码和官方demo不一样的地方,并没有发现什么异常之处,网上搜索也没有发现这种异常记录。由于刚接触JS,不是很熟,就从问题开始下手,找到出现问题的代码,开始调试。

错误日志中提示是在 yzal/index.js 文件中发生异常,找到出现异常的代码如下所示:

function pumpFileDataReadStream(self, entry, readStream) {
 var crc32Watcher = new Crc32Watcher();
 var uncompressedSizeCounter = new ByteCounter();
 var compressor = entry.compress ? new zlib.DeflateRaw() : new PassThrough();
 var compressedSizeCounter = new ByteCounter();
 readStream.pipe(crc32Watcher)
      .pipe(uncompressedSizeCounter)
      .pipe(compressor)
      .pipe(compressedSizeCounter)
      .pipe(self.outputStream, {end: false});
 compressedSizeCounter.on("end", function() {
  entry.crc32 = crc32Watcher.crc32;
  if (entry.uncompressedSize == null) {
   entry.uncompressedSize = uncompressedSizeCounter.byteCount;
  } else {
   // 异常从这里抛出来的
   if (entry.uncompressedSize !== uncompressedSizeCounter.byteCount) return self.emit("error", new Error("file data stream has unexpected number of bytes"));
  }
  entry.compressedSize = compressedSizeCounter.byteCount;
  self.outputStreamCursor += entry.compressedSize;
  writeToOutputStream(self, entry.getDataDescriptor());
  entry.state = Entry.FILE_DATA_DONE;
  pumpEntries(self);
 });
}

从上面代码可以看出来: uncompressedSizeCounter.byteCount 是从 pumpFileDataReadStream() 函数 readStream 参数中获取的属性值,而 entry.uncompressedSize 也是函数的 entry 参数中获取的属性。接着找 pumpFileDataReadStream() 函数是从哪里调用的。

通过输出日志得出 pumpFileDataReadStream() 函数是在以下面的代码中被调用的:

ZipFile.prototype.addFile = function(realPath, metadataPath, options) {
 var self = this;
 metadataPath = validateMetadataPath(metadataPath, false);
 if (options == null) options = {};
 var entry = new Entry(metadataPath, false, options);
 self.entries.push(entry);
 fs.stat(realPath, function(err, stats) {
  if (err) return self.emit("error", err);
  if (!stats.isFile()) return self.emit("error", new Error("not a file: " + realPath));
  // 这里是文件的大小
  entry.uncompressedSize = stats.size;
  if (options.mtime == null) entry.setLastModDate(stats.mtime);
  if (options.mode == null) entry.setFileAttributesMode(stats.mode);
  entry.setFileDataPumpFunction(function() {
   // readStream在这里创建的
   var readStream = fs.createReadStream(realPath);
   entry.state = Entry.FILE_DATA_IN_PROGRESS;
   readStream.on("error", function(err) {
    self.emit("error", err);
   });
   // 在这里被调用
   pumpFileDataReadStream(self, entry, readStream);
  });
  pumpEntries(self);
 });
};

从上面代码可以看出来 entry.uncompressedSize 是stats.size,即文件的大小, readStream 是创建的文件流。但是在什么情况下两者会不一样呢?感觉只可能在文件还没有读取完,但是是什么原因导致这种情况发生?由于对JS接触的时间不长,没有进行深入分析。最后在抛出异常的上面一行用 console.log 将两个属性的大小值都输出,代码如下所示:

if (entry.uncompressedSize == null) {
   entry.uncompressedSize = uncompressedSizeCounter.byteCount;
} else {
 // 增加日志输出
 console.log("entry size: " + entry.uncompressedSize + ", uncompressedSize: " + uncompressedSizeCounter.byteCount);
 if (entry.uncompressedSize !== uncompressedSizeCounter.byteCount) return self.emit("error", new Error("file data stream has unexpected number of bytes"));
}

在 archive.finalize() 时和 output 写入流的 close 事件时(详细参照官方的示例代码),分别加上日志输出,代码如下所示:

archive.finalize().then(() => {
 // 到这里认为是压缩完成,进行后续处理,实际并没有,参照后面分析
 console.log("finalize");
 // anotherProcess();
}).catch(err => {
  // 压缩出现异常
});
output.on('close', function() {
 console.log('close');
 // 这个业务函数与上面finalize函数中的是互斥,不会同时存在
 anotherProcess();
});

最后分别将 anotherProcess() 函数加到两个异步回调中执行,发现在 close 事件执行时,两个size输出的大小一致,都是文件的大小。而在 finalize 场景测试发现 uncompressedSize 要小于文件的大小。最后将 anotherProcess() 函数放在 close 事件回调函数中执行,问题解决。

总结

以上所述是小编给大家介绍的Nodejs使用archiver-zip-encrypted库加密压缩文件时报错,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

NodeJs 相关文章推荐
Nodejs极简入门教程(三):进程
Oct 27 NodeJs
nodejs实现HTTPS发起POST请求
Apr 23 NodeJs
详解nodejs微信公众号开发——5.素材管理接口
Apr 11 NodeJs
NodeJS使用七牛云存储上传文件的方法
Jul 24 NodeJs
浅谈nodejs中的类定义和继承的套路
Jul 26 NodeJs
nodejs使用express获取get和post传值及session验证的方法
Nov 09 NodeJs
nodejs项目windows下开机自启动的方法
Nov 22 NodeJs
nodejs搭建本地服务器轻松解决跨域问题
Mar 21 NodeJs
Nodejs实现爬虫抓取数据实例解析
Jul 05 NodeJs
Nodejs Express 通过log4js写日志到Logstash(ELK)
Aug 30 NodeJs
Nodejs实现用户注册功能
Apr 14 NodeJs
nodejs简单抓包工具使用详解
Aug 23 NodeJs
NodeJs crypto加密制作token的实现代码
Nov 15 #NodeJs
Nodejs技巧之Exceljs表格操作用法示例
Nov 06 #NodeJs
NodeJS http模块用法示例【创建web服务器/客户端】
Nov 05 #NodeJs
nodejs实现UDP组播示例方法
Nov 04 #NodeJs
nodejs dgram模块广播+组播的实现示例
Nov 04 #NodeJs
Nodejs实现图片上传、压缩预览、定时删除功能
Oct 25 #NodeJs
nodejs语言实现验证码生成功能的示例代码
Oct 13 #NodeJs
You might like
php部分常见问题总结
2008/03/27 PHP
php 智能404跳转代码,适合换域名没改变目录的网站
2010/06/04 PHP
PHP print类函数使用总结
2010/06/25 PHP
php class中self,parent,this的区别以及实例介绍
2013/04/24 PHP
php 实现简单的登录功能示例【基于thinkPHP框架】
2019/12/02 PHP
Jquery图片延迟加载插件jquery.lazyload.js的使用方法
2014/05/21 Javascript
node.js中的fs.openSync方法使用说明
2014/12/17 Javascript
jQuery学习笔记之jQuery中的$
2015/01/19 Javascript
jQuery点击其他地方时菜单消失的实现方法
2016/04/22 Javascript
AngularJS ng-app 指令实例详解
2016/07/30 Javascript
JS中对Cookie的操作详解
2016/08/05 Javascript
实现隔行换色效果的两种方式【实用】
2016/11/27 Javascript
jquery实现输入框实时输入触发事件代码
2016/12/21 Javascript
js实现图片旋转 js滚动鼠标中间对图片放大缩小
2017/07/05 Javascript
Vue2.0基于vue-cli+webpack同级组件之间的通信教程(推荐)
2017/09/14 Javascript
vue实现前进刷新后退不刷新效果
2018/01/26 Javascript
使用express+multer实现node中的图片上传功能
2018/02/02 Javascript
Vue 中mixin 的用法详解
2018/04/23 Javascript
js array数组对象操作方法汇总
2019/03/18 Javascript
关于JavaScript 数组你应该知道的事情(推荐)
2019/04/10 Javascript
[54:24]Optic vs TNC 2018国际邀请赛小组赛BO2 第二场
2018/08/18 DOTA
python中使用sys模板和logging模块获取行号和函数名的方法
2014/04/15 Python
python pands实现execl转csv 并修改csv指定列的方法
2018/12/12 Python
Python 把序列转换为元组的函数tuple方法
2019/06/27 Python
python+pygame实现坦克大战
2019/09/10 Python
python pprint模块中print()和pprint()两者的区别
2020/02/10 Python
python实现贪吃蛇双人大战
2020/04/18 Python
Python Tkinter图形工具使用方法及实例解析
2020/06/15 Python
教你怎样写好自我评价
2013/10/05 职场文书
医药工作岗位求职信分享
2013/12/31 职场文书
大学生职业生涯规划方案
2014/01/03 职场文书
乡镇干部个人对照检查材料(群众路线)
2014/09/26 职场文书
工伤私了协议书范本
2014/11/24 职场文书
学校开除通知书
2015/04/25 职场文书
外出考察学习心得体会
2016/01/18 职场文书
基于Python实现射击小游戏的制作
2022/04/06 Python