FileDecompressor.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. const fs = require('fs-extra');
  2. const zlib = require('zlib');
  3. const crypto = require('crypto');
  4. const path = require('path');
  5. const utils = require('./utils');
  6. const extractZip = require('extract-zip');
  7. const unbzip2Stream = require('unbzip2-stream');
  8. const FileDetector = require('./FileDetector');
  9. class FileDecompressor {
  10. constructor() {
  11. this.detector = new FileDetector();
  12. }
  13. async decompressNested(filename, outputDir) {
  14. await fs.ensureDir(outputDir);
  15. const fileType = await this.detector.detectFile(filename);
  16. let result = {
  17. sourceFile: filename,
  18. sourceFileType: fileType,
  19. selectedFile: filename,
  20. filesDir: outputDir,
  21. files: []
  22. };
  23. if (!fileType || !(fileType.ext == 'zip' || fileType.ext == 'bz2' || fileType.ext == 'gz')) {
  24. return result;
  25. }
  26. result.files = await this.decompressTar(fileType.ext, filename, outputDir);
  27. let sel = filename;
  28. let max = 0;
  29. if (result.files.length) {
  30. //ищем файл с максимальным размером
  31. for (let file of result.files) {
  32. if (file.size > max) {
  33. sel = `${outputDir}/${file.path}`;
  34. max = file.size;
  35. }
  36. }
  37. }
  38. result.selectedFile = sel;
  39. if (sel != filename) {
  40. result.nesting = await this.decompressNested(sel, `${outputDir}/${utils.randomHexString(10)}`);
  41. }
  42. return result;
  43. }
  44. async decompressTar(fileExt, filename, outputDir) {
  45. return await this.decompress(fileExt, filename, outputDir);
  46. }
  47. async decompress(fileExt, filename, outputDir) {
  48. let files = [];
  49. switch (fileExt) {
  50. case 'zip':
  51. files = await this.unZip(filename, outputDir);
  52. break;
  53. case 'bz2':
  54. files = await this.unBz2(filename, outputDir);
  55. break;
  56. case 'gz':
  57. files = await this.unGz(filename, outputDir);
  58. break;
  59. case 'tar':
  60. files = await this.unTar(filename, outputDir);
  61. break;
  62. default:
  63. throw new Error(`FileDecompressor: неизвестный формат файла '${fileExt}'`);
  64. }
  65. return files;
  66. }
  67. async unZip(filename, outputDir) {
  68. return new Promise((resolve, reject) => {
  69. const files = [];
  70. extractZip(filename, {
  71. dir: outputDir,
  72. onEntry: (entry) => {
  73. files.push({path: entry.fileName, size: entry.uncompressedSize});
  74. }
  75. }, (err) => {
  76. if (err)
  77. reject(err);
  78. resolve(files);
  79. });
  80. });
  81. }
  82. async unBz2(filename, outputDir) {
  83. return new Promise((resolve, reject) => {
  84. const file = {path: path.basename(filename)};
  85. const outFilename = `${outputDir}/${file.path}`;
  86. const inputStream = fs.createReadStream(filename);
  87. const outputStream = fs.createWriteStream(outFilename);
  88. outputStream.on('close', async() => {
  89. try {
  90. file.size = (await fs.stat(outFilename)).size;
  91. } catch (e) {
  92. reject(e);
  93. }
  94. resolve([file]);
  95. });
  96. inputStream.on('error', (err) => {
  97. reject(err);
  98. });
  99. outputStream.on('error', (err) => {
  100. reject(err);
  101. });
  102. inputStream.pipe(unbzip2Stream()).pipe(outputStream);
  103. });
  104. }
  105. async unGz(filename, outputDir) {
  106. }
  107. async unTar(filename, outputDir) {
  108. }
  109. async gzipBuffer(buf) {
  110. return new Promise((resolve, reject) => {
  111. zlib.gzip(buf, {level: 1}, (err, result) => {
  112. if (err) reject(err);
  113. resolve(result);
  114. });
  115. });
  116. }
  117. async gzipFileIfNotExists(filename, outDir) {
  118. const buf = await fs.readFile(filename);
  119. const hash = crypto.createHash('sha256').update(buf).digest('hex');
  120. const outFilename = `${outDir}/${hash}`;
  121. if (!await fs.pathExists(outFilename)) {
  122. await fs.writeFile(outFilename, await this.gzipBuffer(buf))
  123. } else {
  124. await utils.touchFile(outFilename);
  125. }
  126. return outFilename;
  127. }
  128. }
  129. module.exports = FileDecompressor;