Skip to content

Commit

Permalink
Fix for mv: works accross filesystems
Browse files Browse the repository at this point in the history
Now mv use copy + unlink instead of rename for moving temporary files.
  • Loading branch information
RomanBurunkov committed Mar 7, 2019
1 parent 02eea18 commit a4f13ff
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 38 deletions.
54 changes: 23 additions & 31 deletions lib/fileFactory.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,12 @@
'use strict';

const fs = require('fs');
const path = require('path');
const streamifier = require('streamifier');

/**
* Returns true if argument is function.
*/
const isFunc = func => func && func.constructor && func.call && func.apply;

/**
* Creates a folder for file specified in the path variable
* @param {Object} fileUploadOptions
* @param {String} filePath
*/
const checkAndMakeDir = function(fileUploadOptions, filePath){
if (fileUploadOptions && fileUploadOptions.createParentPath) {
const parentPath = path.dirname(filePath);
if (!fs.existsSync(parentPath)) {
fs.mkdirSync(parentPath);
}
}
};
const {
isFunc,
checkAndMakeDir,
copyFile,
saveBufferToFile
} = require('./utilities.js');

/**
* Returns Local function that moves the file to a different location on the filesystem
Expand All @@ -33,12 +18,20 @@ const moveFromTemp = function(filePath, options) {
return function(successFunc, errorFunc){
// Set errorFunc to the same value as successFunc for callback mode.
errorFunc = isFunc(errorFunc) ? errorFunc : successFunc;
fs.rename(options.tempFilePath, filePath, function(err){
// Copy temporary file.
copyFile(options.tempFilePath, filePath, function(err){
if (err) {
errorFunc(err);
} else {
successFunc();
return;
}
// Delete temporary file.
fs.unlink(options.tempFilePath, (err) => {
if (err) {
errorFunc(err);
} else {
successFunc();
}
});
});
};
};
Expand All @@ -53,13 +46,12 @@ const moveFromBuffer = function(filePath, options) {
return function(successFunc, errorFunc){
// Set errorFunc to the same value as successFunc for callback mode.
errorFunc = isFunc(errorFunc) ? errorFunc : successFunc;
const fstream = fs.createWriteStream(filePath);
streamifier.createReadStream(options.buffer).pipe(fstream);
fstream.on('error', function(error) {
errorFunc(error);
});
fstream.on('close', function() {
successFunc();
saveBufferToFile(options.buffer, filePath, function(err){
if (err) {
errorFunc(err);
} else {
successFunc();
}
});
};
};
Expand Down
13 changes: 6 additions & 7 deletions lib/tempFileHandler.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const {checkAndMakeDir} = require('./utilities.js');

module.exports = function(options, fieldname, filename) {
const dir = path.normalize(options.tempFileDir || process.cwd() + '/tmp/');
let hash = crypto.createHash('md5');
const tempFilePath = path.join(dir, 'tmp' + Date.now());

checkAndMakeDir({createParentPath: true}, tempFilePath);

if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
let tempFilePath = path.join(dir, 'tmp' + Date.now());
let hash = crypto.createHash('md5');
let writeStream = fs.createWriteStream(tempFilePath);
let fileSize = 0; // eslint-disable-line

Expand Down Expand Up @@ -37,8 +37,7 @@ module.exports = function(options, fieldname, filename) {
},
complete: function(){
writeStream.end();
//return empty buffer since data has been uploaded to the temporary file.
return Buffer.concat([]);
return Buffer.concat([]); //return empty buffer since data has been uploaded to the temporary file.
},
cleanup: function(){
writeStream.end();
Expand Down
78 changes: 78 additions & 0 deletions lib/utilities.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
'use strict';

const fs = require('fs');
const path = require('path');
const streamifier = require('streamifier');


/**
* Returns true if argument is function.
*/
const isFunc = func => func && func.constructor && func.call && func.apply ? true: false;

/**
* Creates a folder for file specified in the path variable
* @param {Object} fileUploadOptions
* @param {String} filePath
*/
const checkAndMakeDir = function(fileUploadOptions, filePath){
//Check upload options were set.
if (!fileUploadOptions) return false;
if (!fileUploadOptions.createParentPath) return false;
//Check whether folder for the file exists.
if (!filePath) return false;
const parentPath = path.dirname(filePath);
//Create folder if it is not exists.
if(!fs.existsSync(parentPath)) fs.mkdirSync(parentPath);
return true;
};

/**
* Copy file via streams
* @param {String} src - Path to the source file
* @param {String} dst - Path to the destination file.
*/
const copyFile = function(src, dst, callback){
//cbCalled flag and runCb helps to run cb only once.
let cbCalled = false;
let runCb = (err) => {
if(cbCalled) return;
cbCalled = true;
callback(err);
};
//Create read stream
let readable = fs.createReadStream(src);
readable.on('error', runCb);
//Create write stream
let writable = fs.createWriteStream(dst);
writable.on('error', (err)=>{
readable.destroy();
runCb(err);
});
writable.on('close', () => runCb());
//Copieng file via piping streams.
readable.pipe(writable);
};

/**
* Save buffer data to a file.
* @param {Buffer} buffer - buffer to save to a file.
* @param {String} filePath - path to a file.
*/
const saveBufferToFile = function(buffer, filePath, callback){
if(!Buffer.isBuffer(buffer)){
callback(new Error('buffer variable should be a Buffer!'));
return;
}
const fstream = fs.createWriteStream(filePath);
streamifier.createReadStream(buffer).pipe(fstream);
fstream.on('error', error => callback(error));
fstream.on('close', () => callback());
};

module.exports = {
isFunc,
checkAndMakeDir,
copyFile,
saveBufferToFile
};

0 comments on commit a4f13ff

Please sign in to comment.