123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- const debug = require('debug')('streamroller:moveAndMaybeCompressFile');
- const fs = require('fs-extra');
- const zlib = require('zlib');
- const _parseOption = function(rawOptions){
- const defaultOptions = {
- mode: parseInt("0600", 8),
- compress: false,
- };
- const options = Object.assign({}, defaultOptions, rawOptions);
- debug(`_parseOption: moveAndMaybeCompressFile called with option=${JSON.stringify(options)}`);
- return options;
- };
- const moveAndMaybeCompressFile = async (
- sourceFilePath,
- targetFilePath,
- options
- ) => {
- options = _parseOption(options);
- if (sourceFilePath === targetFilePath) {
- debug(`moveAndMaybeCompressFile: source and target are the same, not doing anything`);
- return;
- }
- if (await fs.pathExists(sourceFilePath)) {
- debug(
- `moveAndMaybeCompressFile: moving file from ${sourceFilePath} to ${targetFilePath} ${
- options.compress ? "with" : "without"
- } compress`
- );
- if (options.compress) {
- await new Promise((resolve, reject) => {
- let isCreated = false;
- // to avoid concurrency, the forked process which can create the file will proceed (using flags wx)
- const writeStream = fs.createWriteStream(targetFilePath, { mode: options.mode, flags: "wx" })
- // wait until writable stream is valid before proceeding to read
- .on("open", () => {
- isCreated = true;
- const readStream = fs.createReadStream(sourceFilePath)
- // wait until readable stream is valid before piping
- .on("open", () => {
- readStream.pipe(zlib.createGzip()).pipe(writeStream);
- })
- .on("error", (e) => {
- debug(`moveAndMaybeCompressFile: error reading ${sourceFilePath}`, e);
- // manually close writable: https://nodejs.org/api/stream.html#readablepipedestination-options
- writeStream.destroy(e);
- });
- })
- .on("finish", () => {
- debug(`moveAndMaybeCompressFile: finished compressing ${targetFilePath}, deleting ${sourceFilePath}`);
- // delete sourceFilePath
- fs.unlink(sourceFilePath)
- .then(resolve)
- .catch((e) => {
- debug(`moveAndMaybeCompressFile: error deleting ${sourceFilePath}, truncating instead`, e);
- // fallback to truncate
- fs.truncate(sourceFilePath)
- .then(resolve)
- .catch((e) => {
- debug(`moveAndMaybeCompressFile: error truncating ${sourceFilePath}`, e);
- reject(e);
- });
- });
- })
- .on("error", (e) => {
- if (!isCreated) {
- debug(`moveAndMaybeCompressFile: error creating ${targetFilePath}`, e);
- // do not do anything if handled by another forked process
- reject(e);
- } else {
- debug(`moveAndMaybeCompressFile: error writing ${targetFilePath}, deleting`, e);
- // delete targetFilePath (taking as nothing happened)
- fs.unlink(targetFilePath)
- .then(() => { reject(e); })
- .catch((e) => {
- debug(`moveAndMaybeCompressFile: error deleting ${targetFilePath}`, e);
- reject(e);
- });
- }
- });
- }).catch(() => {});
- } else {
- debug(`moveAndMaybeCompressFile: renaming ${sourceFilePath} to ${targetFilePath}`);
- try {
- await fs.move(sourceFilePath, targetFilePath, { overwrite: true });
- } catch (e) {
- debug(`moveAndMaybeCompressFile: error renaming ${sourceFilePath} to ${targetFilePath}`, e);
- debug(`moveAndMaybeCompressFile: trying copy+truncate instead`);
- await fs.copy(sourceFilePath, targetFilePath, { overwrite: true });
- await fs.truncate(sourceFilePath);
- }
- }
- }
- };
- module.exports = moveAndMaybeCompressFile;
|