mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-04-22 08:26:16 -04:00
import/export: Promisify Abiword and LibreOffice conversion
This commit is contained in:
parent
b321267e66
commit
b2c0837cf5
4 changed files with 67 additions and 91 deletions
|
@ -98,8 +98,7 @@ exports.doExport = async (req, res, padId, readOnlyId, type) => {
|
||||||
settings.soffice != null ? require('../utils/LibreOffice')
|
settings.soffice != null ? require('../utils/LibreOffice')
|
||||||
: settings.abiword != null ? require('../utils/Abiword')
|
: settings.abiword != null ? require('../utils/Abiword')
|
||||||
: null;
|
: null;
|
||||||
// @TODO no Promise interface for converters (yet)
|
await converter.convertFile(srcFile, destFile, type);
|
||||||
await util.promisify(converter.convertFile)(srcFile, destFile, type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// send the file
|
// send the file
|
||||||
|
|
|
@ -179,17 +179,12 @@ const doImport = async (req, res, padId) => {
|
||||||
// if no converter only rename
|
// if no converter only rename
|
||||||
await fs.rename(srcFile, destFile);
|
await fs.rename(srcFile, destFile);
|
||||||
} else {
|
} else {
|
||||||
// @TODO - no Promise interface for converters (yet)
|
try {
|
||||||
await new Promise((resolve, reject) => {
|
await converter.convertFile(srcFile, destFile, exportExtension);
|
||||||
converter.convertFile(srcFile, destFile, exportExtension, (err) => {
|
} catch (err) {
|
||||||
// catch convert errors
|
logger.warn(`Converting Error: ${err.stack || err}`);
|
||||||
if (err) {
|
throw new ImportError('convertFailed');
|
||||||
logger.warn(`Converting Error: ${err.stack || err}`);
|
}
|
||||||
return reject(new ImportError('convertFailed'));
|
|
||||||
}
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,28 +24,24 @@ const async = require('async');
|
||||||
const settings = require('./Settings');
|
const settings = require('./Settings');
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
|
|
||||||
let doConvertTask;
|
|
||||||
|
|
||||||
// on windows we have to spawn a process for each convertion,
|
// on windows we have to spawn a process for each convertion,
|
||||||
// cause the plugin abicommand doesn't exist on this platform
|
// cause the plugin abicommand doesn't exist on this platform
|
||||||
if (os.type().indexOf('Windows') > -1) {
|
if (os.type().indexOf('Windows') > -1) {
|
||||||
doConvertTask = (task, callback) => {
|
exports.convertFile = async (srcFile, destFile, type) => {
|
||||||
const abiword = spawn(settings.abiword, [`--to=${task.destFile}`, task.srcFile]);
|
const abiword = spawn(settings.abiword, [`--to=${destFile}`, srcFile]);
|
||||||
let stdoutBuffer = '';
|
let stdoutBuffer = '';
|
||||||
abiword.stdout.on('data', (data) => { stdoutBuffer += data.toString(); });
|
abiword.stdout.on('data', (data) => { stdoutBuffer += data.toString(); });
|
||||||
abiword.stderr.on('data', (data) => { stdoutBuffer += data.toString(); });
|
abiword.stderr.on('data', (data) => { stdoutBuffer += data.toString(); });
|
||||||
abiword.on('exit', (code) => {
|
await new Promise((resolve, reject) => {
|
||||||
if (code !== 0) return callback(new Error(`Abiword died with exit code ${code}`));
|
abiword.on('exit', (code) => {
|
||||||
if (stdoutBuffer !== '') {
|
if (code !== 0) return reject(new Error(`Abiword died with exit code ${code}`));
|
||||||
console.log(stdoutBuffer);
|
if (stdoutBuffer !== '') {
|
||||||
}
|
console.log(stdoutBuffer);
|
||||||
callback();
|
}
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.convertFile = (srcFile, destFile, type, callback) => {
|
|
||||||
doConvertTask({srcFile, destFile, type}, callback);
|
|
||||||
};
|
|
||||||
// on unix operating systems, we can start abiword with abicommand and
|
// on unix operating systems, we can start abiword with abicommand and
|
||||||
// communicate with it via stdin/stdout
|
// communicate with it via stdin/stdout
|
||||||
// thats much faster, about factor 10
|
// thats much faster, about factor 10
|
||||||
|
@ -85,7 +81,7 @@ if (os.type().indexOf('Windows') > -1) {
|
||||||
};
|
};
|
||||||
}, 1);
|
}, 1);
|
||||||
|
|
||||||
exports.convertFile = (srcFile, destFile, type, callback) => {
|
exports.convertFile = async (srcFile, destFile, type) => {
|
||||||
queue.push({srcFile, destFile, type}, callback);
|
await queue.pushAsync({srcFile, destFile, type});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const async = require('async');
|
const async = require('async');
|
||||||
const fs = require('fs');
|
const fs = require('fs').promises;
|
||||||
const log4js = require('log4js');
|
const log4js = require('log4js');
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
@ -27,57 +27,55 @@ const spawn = require('child_process').spawn;
|
||||||
|
|
||||||
const libreOfficeLogger = log4js.getLogger('LibreOffice');
|
const libreOfficeLogger = log4js.getLogger('LibreOffice');
|
||||||
|
|
||||||
const doConvertTask = (task, callback) => {
|
const doConvertTask = async (task) => {
|
||||||
const tmpDir = os.tmpdir();
|
const tmpDir = os.tmpdir();
|
||||||
|
|
||||||
async.series([
|
libreOfficeLogger.debug(
|
||||||
(callback) => {
|
`Converting ${task.srcFile} to format ${task.type}. The result will be put in ${tmpDir}`);
|
||||||
libreOfficeLogger.debug(
|
const soffice = spawn(settings.soffice, [
|
||||||
`Converting ${task.srcFile} to format ${task.type}. The result will be put in ${tmpDir}`
|
'--headless',
|
||||||
);
|
'--invisible',
|
||||||
const soffice = spawn(settings.soffice, [
|
'--nologo',
|
||||||
'--headless',
|
'--nolockcheck',
|
||||||
'--invisible',
|
'--writer',
|
||||||
'--nologo',
|
'--convert-to',
|
||||||
'--nolockcheck',
|
task.type,
|
||||||
'--writer',
|
task.srcFile,
|
||||||
'--convert-to',
|
'--outdir',
|
||||||
task.type,
|
tmpDir,
|
||||||
task.srcFile,
|
]);
|
||||||
'--outdir',
|
// Soffice/libreoffice is buggy and often hangs.
|
||||||
tmpDir,
|
// To remedy this we kill the spawned process after a while.
|
||||||
]);
|
const hangTimeout = setTimeout(() => {
|
||||||
// Soffice/libreoffice is buggy and often hangs.
|
soffice.stdin.pause(); // required to kill hanging threads
|
||||||
// To remedy this we kill the spawned process after a while.
|
soffice.kill();
|
||||||
const hangTimeout = setTimeout(() => {
|
}, 120000);
|
||||||
soffice.stdin.pause(); // required to kill hanging threads
|
let stdoutBuffer = '';
|
||||||
soffice.kill();
|
soffice.stdout.on('data', (data) => { stdoutBuffer += data.toString(); });
|
||||||
}, 120000);
|
soffice.stderr.on('data', (data) => { stdoutBuffer += data.toString(); });
|
||||||
let stdoutBuffer = '';
|
await new Promise((resolve, reject) => {
|
||||||
soffice.stdout.on('data', (data) => { stdoutBuffer += data.toString(); });
|
soffice.on('exit', (code) => {
|
||||||
soffice.stderr.on('data', (data) => { stdoutBuffer += data.toString(); });
|
clearTimeout(hangTimeout);
|
||||||
soffice.on('exit', (code) => {
|
if (code !== 0) {
|
||||||
clearTimeout(hangTimeout);
|
return reject(
|
||||||
if (code !== 0) {
|
new Error(`LibreOffice died with exit code ${code} and message: ${stdoutBuffer}`));
|
||||||
return callback(
|
}
|
||||||
new Error(`LibreOffice died with exit code ${code} and message: ${stdoutBuffer}`));
|
resolve();
|
||||||
}
|
});
|
||||||
callback();
|
});
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
(callback) => {
|
const filename = path.basename(task.srcFile);
|
||||||
const filename = path.basename(task.srcFile);
|
const sourceFile = `${filename.substr(0, filename.lastIndexOf('.'))}.${task.fileExtension}`;
|
||||||
const sourceFile = `${filename.substr(0, filename.lastIndexOf('.'))}.${task.fileExtension}`;
|
const sourcePath = path.join(tmpDir, sourceFile);
|
||||||
const sourcePath = path.join(tmpDir, sourceFile);
|
libreOfficeLogger.debug(`Renaming ${sourcePath} to ${task.destFile}`);
|
||||||
libreOfficeLogger.debug(`Renaming ${sourcePath} to ${task.destFile}`);
|
await fs.rename(sourcePath, task.destFile);
|
||||||
fs.rename(sourcePath, task.destFile, callback);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Conversion tasks will be queued up, so we don't overload the system
|
// Conversion tasks will be queued up, so we don't overload the system
|
||||||
const queue = async.queue(doConvertTask, 1);
|
const queue = async.queue(
|
||||||
|
// For some reason util.callbackify() throws "TypeError [ERR_INVALID_ARG_TYPE]: The last
|
||||||
|
// argument must be of type Function. Received type object" on Node.js 10.x.
|
||||||
|
(task, cb) => doConvertTask(task).then(() => cb(), (err) => cb(err || new Error(err))), 1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a file from one type to another
|
* Convert a file from one type to another
|
||||||
|
@ -87,7 +85,7 @@ const queue = async.queue(doConvertTask, 1);
|
||||||
* @param {String} type The type to convert into
|
* @param {String} type The type to convert into
|
||||||
* @param {Function} callback Standard callback function
|
* @param {Function} callback Standard callback function
|
||||||
*/
|
*/
|
||||||
exports.convertFile = (srcFile, destFile, type, callback) => {
|
exports.convertFile = async (srcFile, destFile, type) => {
|
||||||
// Used for the moving of the file, not the conversion
|
// Used for the moving of the file, not the conversion
|
||||||
const fileExtension = type;
|
const fileExtension = type;
|
||||||
|
|
||||||
|
@ -107,21 +105,9 @@ exports.convertFile = (srcFile, destFile, type, callback) => {
|
||||||
// to avoid `Error: no export filter for /tmp/xxxx.doc` error
|
// to avoid `Error: no export filter for /tmp/xxxx.doc` error
|
||||||
if (type === 'doc') {
|
if (type === 'doc') {
|
||||||
const intermediateFile = destFile.replace(/\.doc$/, '.odt');
|
const intermediateFile = destFile.replace(/\.doc$/, '.odt');
|
||||||
async.series([
|
await queue.pushAsync({srcFile, destFile: intermediateFile, type: 'odt', fileExtension: 'odt'});
|
||||||
(callback) => queue.push({
|
await queue.pushAsync({srcFile: intermediateFile, destFile, type, fileExtension});
|
||||||
srcFile,
|
|
||||||
destFile: intermediateFile,
|
|
||||||
type: 'odt',
|
|
||||||
fileExtension: 'odt',
|
|
||||||
}, callback),
|
|
||||||
(callback) => queue.push({
|
|
||||||
srcFile: intermediateFile,
|
|
||||||
destFile,
|
|
||||||
type,
|
|
||||||
fileExtension,
|
|
||||||
}, callback),
|
|
||||||
], callback);
|
|
||||||
} else {
|
} else {
|
||||||
queue.push({srcFile, destFile, type, fileExtension}, callback);
|
await queue.pushAsync({srcFile, destFile, type, fileExtension});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue