Adding LibreOffice conversion support (WIP)
This commit is contained in:
@@ -2,6 +2,7 @@ import express, { Request, Response } from 'express';
|
||||
|
||||
//import workflow from './workflow-controller';
|
||||
import operations from './operations-controller';
|
||||
import conversions from './conversions-controller';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@@ -11,6 +12,7 @@ router.get("/", (req: Request, res: Response) => {
|
||||
});
|
||||
|
||||
router.use("/operations", operations);
|
||||
router.use("/conversions", conversions);
|
||||
//router.use("/workflow", workflow);
|
||||
|
||||
export default router;
|
||||
27
server-node/src/routes/api/conversions-controller.ts
Normal file
27
server-node/src/routes/api/conversions-controller.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
|
||||
import { respondWithPdfFile, response_mustHaveExactlyOneFile, response_dependencyNotConfigured } from '../../utils/endpoint-utils';
|
||||
import { fileToPdf, isLibreOfficeInstalled } from '../../utils/libre-office-utils';
|
||||
|
||||
import express, { Request, Response } from 'express';
|
||||
const router = express.Router();
|
||||
import multer from 'multer';
|
||||
const upload = multer();
|
||||
import Joi from 'joi';
|
||||
|
||||
router.post('/file-to-pdf', upload.single("file"), async function(req: Request, res: Response) {
|
||||
if (!req.file) {
|
||||
response_mustHaveExactlyOneFile(res);
|
||||
return;
|
||||
}
|
||||
|
||||
const isInstalled = await isLibreOfficeInstalled();
|
||||
if (isInstalled) {
|
||||
const outputFile = await fileToPdf(req.file.buffer, req.file.originalname);
|
||||
respondWithPdfFile(res, outputFile);
|
||||
return;
|
||||
}
|
||||
|
||||
response_dependencyNotConfigured(res, "LibreOffice");
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -2,14 +2,18 @@
|
||||
import { Response } from 'express';
|
||||
import { PdfFile } from '@stirling-pdf/shared-operations/wrappers/PdfFile'
|
||||
|
||||
export async function respondWithFile(res: Response, bytes: Uint8Array, name: string, mimeType: string): Promise<void> {
|
||||
res.writeHead(200, {
|
||||
'Content-Type': mimeType,
|
||||
'Content-disposition': 'attachment;filename=' + name,
|
||||
'Content-Length': bytes.length
|
||||
});
|
||||
res.end(bytes);
|
||||
}
|
||||
|
||||
export async function respondWithPdfFile(res: Response, file: PdfFile): Promise<void> {
|
||||
const byteFile = await file.convertToByteArrayFile();
|
||||
res.writeHead(200, {
|
||||
'Content-Type': "application/pdf",
|
||||
'Content-disposition': 'attachment;filename=' + byteFile.filename,
|
||||
'Content-Length': byteFile.byteArray?.length
|
||||
});
|
||||
res.end(byteFile.byteArray)
|
||||
respondWithFile(res, byteFile.byteArray!, byteFile.filename, "application/pdf");
|
||||
}
|
||||
|
||||
export function response_mustHaveExactlyOneFile(res: Response): void {
|
||||
@@ -27,3 +31,12 @@ export function response_mustHaveExactlyOneFile(res: Response): void {
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
export function response_dependencyNotConfigured(res: Response, dependencyName: string): void {
|
||||
res.status(400).send([
|
||||
{
|
||||
"message": `${dependencyName} is not configured correctly on the server.`,
|
||||
"type": "dependency_error",
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
99
server-node/src/utils/libre-office-utils.ts
Normal file
99
server-node/src/utils/libre-office-utils.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
|
||||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import { exec, spawn } from 'child_process'
|
||||
import { PdfFile, fromUint8Array } from '@stirling-pdf/shared-operations/wrappers/PdfFile'
|
||||
|
||||
export async function fileToPdf(byteArray: Uint8Array, filename: string): Promise<PdfFile> {
|
||||
const parentDir = path.join(os.tmpdir(), "StirlingPDF");
|
||||
fs.mkdirSync(parentDir, {recursive: true});
|
||||
const tempDir = fs.mkdtempSync(parentDir+"/");
|
||||
const srcFile = path.join(tempDir, filename);
|
||||
const randFolderName = path.parse(tempDir).base;
|
||||
|
||||
await writeBytesToFile(srcFile, byteArray);
|
||||
|
||||
const messages = await runLibreOfficeCommand(randFolderName, ["--headless","--convert-to","pdf",srcFile,"--outdir",tempDir]);
|
||||
const lastMessage = messages[messages.length-1]
|
||||
const outputFilePath = lastMessage.split(" -> ")[1].split(".pdf")[0]+".pdf";
|
||||
const outputFileName = path.parse(outputFilePath).base;
|
||||
const outputBytes = await readBytesFromFile(outputFilePath);
|
||||
|
||||
fs.rmdirSync(tempDir);
|
||||
|
||||
return fromUint8Array(outputBytes, outputFileName);
|
||||
}
|
||||
|
||||
export function isLibreOfficeInstalled() {
|
||||
return new Promise((resolve, reject) => {
|
||||
exec("libreoffice --version", (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
if (stderr) {
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
const result = stdout.match("LibreOffice ([0-9]+\.){4}.*");
|
||||
resolve(result ? true : false);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function writeBytesToFile(filePath: string, bytes: Uint8Array): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.writeFile(filePath, bytes, function(err) {
|
||||
if(err) {
|
||||
reject(err)
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function readBytesFromFile(filePath: string): Promise<Uint8Array> {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.readFile(filePath, (err, data) => {
|
||||
if (err) {
|
||||
reject(new Error(`Error reading file: ${err.message}`));
|
||||
} else {
|
||||
const uint8Array = new Uint8Array(data);
|
||||
resolve(uint8Array);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function runLibreOfficeCommand(idKey: string, args: string[]): Promise<string[]> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const messageList: string[] = [];
|
||||
|
||||
const process = spawn("libreoffice", args);
|
||||
|
||||
process.stdout.on('data', (data) => {
|
||||
const dataStr = data.toString();
|
||||
console.log(`Progress ${idKey}:`, dataStr);
|
||||
messageList.push(dataStr);
|
||||
});
|
||||
|
||||
process.stderr.on('data', (data) => {
|
||||
console.error(`stderr ${idKey}:`, data.toString());
|
||||
});
|
||||
|
||||
process.on('exit', (code) => {
|
||||
if (code === 0) {
|
||||
resolve(messageList);
|
||||
} else {
|
||||
reject(new Error(`Command failed with exit code ${code}`));
|
||||
}
|
||||
});
|
||||
|
||||
process.on('error', (err) => {
|
||||
reject(err);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user