import fs from 'fs'; import path from 'path'; import streamToPromise from 'stream-to-promise'; import unzipStream, { type Entry } from 'unzip-stream'; import loggerFactory from '~/utils/logger'; import type { ZipFileStat } from '../interfaces/export'; import { tapStreamDataByPromise } from './unzip-stream-utils'; const logger = loggerFactory('growi:services:GrowiBridgeService'); // eslint-disable-line no-unused-vars /** * the service class for bridging GROWIs (export and import) * common properties and methods between export service and import service are defined in this service */ class GrowiBridgeService { crowi: any; encoding: BufferEncoding = 'utf-8'; metaFileName = 'meta.json'; baseDir: string | undefined; constructor(crowi) { this.crowi = crowi; } /** * getter for encoding * * @memberOf GrowiBridgeService * @return {BufferEncoding} encoding */ getEncoding(): BufferEncoding { return this.encoding; } /** * getter for metaFileName * * @memberOf GrowiBridgeService * @return {string} base name of meta file */ getMetaFileName(): string { return this.metaFileName; } /** * get the absolute path to a file * * @memberOf GrowiBridgeService */ getFile(fileName: string, baseDir: string): string { const jsonFile = path.join(baseDir, fileName); // throws err if the file does not exist fs.accessSync(jsonFile); return jsonFile; } /** * parse a zip file * * @memberOf GrowiBridgeService * @param {string} zipFile path to zip file * @return {object} meta{object} and files{Array.} */ async parseZipFile(zipFile: string): Promise { const fileStat = fs.statSync(zipFile); const innerFileStats: Array<{ fileName: string, collectionName: string, size: number }> = []; let meta = {}; const readStream = fs.createReadStream(zipFile); const unzipStreamPipe = readStream.pipe(unzipStream.Parse()); let tapPromise; const unzipEntryStream = unzipStreamPipe.on('entry', (entry: Entry) => { const fileName = entry.path; const size = entry.size; // might be undefined in some archives if (fileName === this.getMetaFileName()) { tapPromise = tapStreamDataByPromise(entry).then((metaBuffer) => { meta = JSON.parse(metaBuffer.toString()); }); } else { innerFileStats.push({ fileName, collectionName: path.basename(fileName, '.json'), size, }); } entry.autodrain(); }); try { await streamToPromise(unzipEntryStream); await tapPromise; } // if zip is broken catch (err) { logger.error(err); return null; } return { meta, fileName: path.basename(zipFile), zipFilePath: zipFile, fileStat, innerFileStats, }; } } export default GrowiBridgeService;