im not using pdf2docx , i’m using libreoffice commands directly for conversion
controller
@Post('pdf-to-word')
@FormDataRequest()
async convertPdfToWord(@Body() { file }: UploadPdfDto) {
const result = await this.fileConvertorService.convertPdfToDocx(
file.buffer,
);
return result;
}
service
async convertPdfToDocx(
pdfBuffer: Buffer,
): Promise<{ fileUrl: string; outputPath: string }> {
let inputPath: string | undefined;
let outputPath: string | undefined;
try {
// Create directories and get paths
const { uploadsDir, tempDir } = await ensureDirectories();
// Create temporary input file
const { fileName, filePath } = await createTempFile(tempDir, pdfBuffer);
inputPath = filePath;
// Determine output path
const outputFileName: string = fileName.replace('.pdf', '.docx');
outputPath = path.join(uploadsDir, outputFileName);
// Execute conversion
const command: string = constructCommand(inputPath, uploadsDir);
await executeLibreOfficeCommand(command);
// Verify and clean up
await verifyConvertedFile(outputPath);
await cleanUpFile(inputPath);
const fileUrl: string = `/uploads/${outputFileName}`;
return { fileUrl, outputPath };
} catch (error) {
if (inputPath && outputPath) {
await handleConversionError(inputPath, outputPath, error as Error);
}
throw error;
}
}
some utilities
import { HttpException, HttpStatus, Logger } from '@nestjs/common';
import { exec } from 'child_process';
import { randomUUID } from 'crypto';
import * as fs from 'fs-extra';
import * as path from 'path';
import { promisify } from 'util';
export async function ensureDirectories() {
const uploadsDir = path.join(process.cwd(), 'uploads');
const tempDir = path.join(process.cwd(), 'temp');
try {
await fs.ensureDir(uploadsDir);
await fs.ensureDir(tempDir);
Logger.log('Directories created successfully');
return { uploadsDir, tempDir };
} catch (error) {
Logger.error('Error ensuring directories:', error);
throw new HttpException(
'Failed to create necessary directories',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
export async function createTempFile(
tempDir: string,
fileBuffer: Buffer,
fileExtension: string = 'pdf',
) {
const fileName = `${randomUUID()}.${fileExtension}`;
const filePath = path.join(tempDir, fileName);
try {
await fs.outputFile(filePath, fileBuffer);
if (!(await fs.pathExists(filePath))) {
throw new HttpException(
`File not created at ${filePath}`,
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
Logger.log(`Temporary file successfully created at ${filePath}`);
return { fileName, filePath };
} catch (error) {
Logger.error(`Error creating temporary file:`, error);
throw error;
}
}
export function constructCommand(inputPath: string, outputDir: string) {
const command = `"C:\\Program Files\\LibreOffice\\program\\soffice.exe" --headless --convert-to docx --infilter="writer_pdf_import" "${inputPath}" --outdir "${outputDir}"`;
return command;
}
export async function executeLibreOfficeCommand(command: string) {
try {
const execAsync = promisify(exec);
const { stdout, stderr } = await execAsync(command);
Logger.log('Conversion stdout:', stdout);
if (stderr) Logger.warn('Conversion stderr:', stderr);
} catch (error) {
throw new HttpException(
`LibreOffice command failed: ${error}`,
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
export async function cleanUpFile(filePath: string) {
try {
if (await fs.pathExists(filePath)) {
await fs.unlink(filePath);
Logger.log(`Successfully cleaned up file: ${filePath}`);
}
} catch (error) {
Logger.error(`Error cleaning up file ${filePath}:`, error);
// Not throwing here to avoid disrupting the main flow
}
}
export async function verifyConvertedFile(filePath: string) {
if (!(await fs.pathExists(filePath))) {
throw new HttpException(
`Converted file not found at ${filePath}`,
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
Logger.log(`Verified converted file exists at: ${filePath}`);
}
export async function handleConversionError(
tempFilePath: string,
outputFilePath: string,
error: Error,
) {
await cleanUpFile(tempFilePath);
await cleanUpFile(outputFilePath);
Logger.error('Conversion process failed:', error);
throw error;
}