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 实现模拟form表单上传文件
Jul 14 NodeJs
Nodejs中session的简单使用及通过session实现身份验证的方法
Feb 04 NodeJs
详解nodejs中exports和module.exports的区别
Feb 17 NodeJs
Express+Nodejs 下的登录拦截实现代码
Jul 01 NodeJs
nodejs 图解express+supervisor+ejs的用法(推荐)
Sep 08 NodeJs
详解IWinter 一个路由转控制器的 Nodejs 库
Nov 15 NodeJs
nodejs 如何手动实现服务器
Aug 20 NodeJs
NodeJS 将文件夹按照存放路径变成一个对应的JSON的方法
Oct 17 NodeJs
NodeJS模块与ES6模块系统语法及注意点详解
Jan 04 NodeJs
nodejs二进制与Buffer的介绍与使用
Jul 11 NodeJs
Nodejs中使用puppeteer控制浏览器中视频播放功能
Aug 26 NodeJs
nodeJs的安装与npm全局环境变量的配置详解
Jan 06 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
ThinkPHP之用户注册登录留言完整实例
2014/07/22 PHP
Yii使用smsto短信接口的函数demo示例
2016/07/13 PHP
laravel框架之数据库查出来的对象实现转化为数组
2019/10/23 PHP
js 单引号 传递方法
2009/06/22 Javascript
javascript forEach通用循环遍历方法
2010/10/11 Javascript
JS按位非(~)运算符与~~运算符的理解分析
2011/07/31 Javascript
JavaScript中的16进制字符(改进)
2011/11/21 Javascript
解决Extjs上传图片无法预览的解决方法
2012/03/22 Javascript
《JavaScript高级编程》学习笔记之object和array引用类型
2015/11/01 Javascript
JS判断是否在微信浏览器打开的简单实例(推荐)
2016/08/24 Javascript
简单理解vue中实例属性vm.$els
2016/12/01 Javascript
vue.js路由跳转详解
2017/08/28 Javascript
Thinkphp5微信小程序获取用户信息接口的实例详解
2017/09/26 Javascript
对node.js中render和send的用法详解
2018/05/14 Javascript
解决vue项目中type=”file“ change事件只执行一次的问题
2018/05/16 Javascript
深入理解vue-class-component源码阅读
2019/02/18 Javascript
详解vue2.6插槽更新v-slot用法总结
2019/03/09 Javascript
JS检测浏览器开发者工具是否打开的方法详解
2020/10/02 Javascript
微信小程序实现购物车小功能
2020/12/30 Javascript
[05:06]TI4西雅图DOTA2前线报道 海涛密探LGD训练
2014/07/09 DOTA
[01:29:46]DOTA2上海特级锦标赛C组资格赛#1 OG VS LGD第二局
2016/02/27 DOTA
零基础写python爬虫之urllib2中的两个重要概念:Openers和Handlers
2014/11/05 Python
python程序快速缩进多行代码方法总结
2019/06/23 Python
基于python 微信小程序之获取已存在模板消息列表
2019/08/05 Python
Python操作Sonqube API获取检测结果并打印过程解析
2019/11/27 Python
基于Tensorflow高阶读写教程
2020/02/10 Python
Python爬虫制作翻译程序的示例代码
2021/02/22 Python
德国圣伯纳德草药屋:Kräuterhaus Sanct Bernhard(有中文站)
2018/08/05 全球购物
运动会表扬稿大全
2014/01/16 职场文书
春节请假条
2014/04/11 职场文书
助人为乐道德模范事迹材料
2014/08/16 职场文书
2014小学生国庆65周年演讲稿
2014/09/21 职场文书
毕业生捐书活动倡议书
2015/04/27 职场文书
2015年酒店年度工作总结
2015/05/23 职场文书
SQL Server基本使用和简单的CRUD操作
2021/04/05 SQL Server
Python人工智能之混合高斯模型运动目标检测详解分析
2021/11/07 Python