From c459997cd9f61b60a686247d511df257ea6f5157 Mon Sep 17 00:00:00 2001 From: Rodrigo Martins Date: Fri, 11 Apr 2025 13:50:32 -0300 Subject: [PATCH] Improve and fix ProvaService.ts Signed-off-by: Rodrigo Martins --- src/server/services/FileValidator.ts | 17 +++++- src/server/services/ProvaService.ts | 26 ++++++++- src/server/services/ReportGenerator.ts | 75 ++++++++++++++++++++++---- 3 files changed, 106 insertions(+), 12 deletions(-) diff --git a/src/server/services/FileValidator.ts b/src/server/services/FileValidator.ts index 640648d..8fdef22 100644 --- a/src/server/services/FileValidator.ts +++ b/src/server/services/FileValidator.ts @@ -1,4 +1,5 @@ import { createHash } from 'crypto'; +import sharp from 'sharp'; export class FileValidator { async validateFile(buffer: Buffer, mimeType: string) { @@ -18,7 +19,7 @@ export class FileValidator { const allowedTypes = [ 'application/pdf', 'image/jpeg', - 'image/png', // Corrigido o tipo MIME para PNG + 'image/png', 'image/gif', 'image.webp' ]; @@ -27,6 +28,20 @@ export class FileValidator { return { valid: false, error: 'Tipo de arquivo não suportado. Use PDF, JPEG, PNG ou GIF.' }; } + // Verificação adicional para JPEG + if (mimeType === 'image/jpeg') { + try { + // Verificar se é um JPEG válido tentando processá-lo + await sharp(buffer).metadata(); + } catch (jpegError) { + console.error('Erro ao validar JPEG:', jpegError); + return { + valid: false, + error: 'O arquivo JPEG parece estar corrompido. Tente salvar a imagem em um formato diferente e enviar novamente.' + }; + } + } + return { valid: true }; } catch (error) { console.error('Erro na validação de arquivo:', error); diff --git a/src/server/services/ProvaService.ts b/src/server/services/ProvaService.ts index 2d837e7..dee8eda 100644 --- a/src/server/services/ProvaService.ts +++ b/src/server/services/ProvaService.ts @@ -294,6 +294,18 @@ export class ProvaService { private async processFileByType(file: Express.Multer.File) { if (file.mimetype.startsWith('image/')) { try { + // Para JPEGs, converter sempre para PNG para evitar problemas + if (file.mimetype === 'image/jpeg') { + return await sharp(file.buffer) + .png() // Converter para PNG é mais seguro + .resize(1200, 900, { + fit: 'inside', + withoutEnlargement: true + }) + .toBuffer(); + } + + // Para outras imagens, manter o formato original return await sharp(file.buffer) .resize(1200, 900, { fit: 'inside', @@ -302,7 +314,19 @@ export class ProvaService { .toBuffer(); } catch (error) { console.error('Erro ao processar imagem com sharp:', error); - return file.buffer; // Fallback para o buffer original + + // Se sharp falhar, tentar abordagem mais simples + try { + console.log('Tentando método alternativo de processamento de imagem...'); + // Converter diretamente para PNG sem redimensionamento + return await sharp(file.buffer) + .png() + .toBuffer(); + } catch (fallbackError) { + console.error('Falha total no processamento da imagem:', fallbackError); + // Em último caso, retornar o buffer original + return file.buffer; + } } } return file.buffer; diff --git a/src/server/services/ReportGenerator.ts b/src/server/services/ReportGenerator.ts index b19f35d..11833f2 100644 --- a/src/server/services/ReportGenerator.ts +++ b/src/server/services/ReportGenerator.ts @@ -5,6 +5,7 @@ import ptBR from 'date-fns/locale/pt-BR'; import { prisma } from '../prismaClient'; import * as fs from 'fs/promises'; import * as path from 'path'; +import sharp from 'sharp'; export class ReportGenerator { async generateReport(data: { @@ -175,7 +176,7 @@ export class ReportGenerator { private async drawImagePage(page: any, data: any, helvetica: any, helveticaBold: any, pdfDoc: PDFDocument) { const height = page.getHeight(); - // Título + // Título e informações básicas (mantidos como estão) page.drawText('Documento Original', { x: 50, y: height - 50, @@ -184,7 +185,6 @@ export class ReportGenerator { color: rgb(0, 0, 0.7) }); - // Informações básicas do arquivo page.drawText(`Arquivo: ${data.file.originalname}`, { x: 50, y: height - 80, @@ -192,7 +192,6 @@ export class ReportGenerator { font: helvetica }); - // Formatação segura de data de registro let timestampStr = 'Data não disponível'; try { if (data.timestamp?.timestamp) { @@ -212,7 +211,6 @@ export class ReportGenerator { font: helvetica }); - // Data de modificação original if (data.metadata?.fileInfo?.modifiedDate || data.metadata?.file_specific?.modified) { const modifiedDate = new Date(data.metadata?.fileInfo?.modifiedDate || data.metadata?.file_specific?.modified); if (!isNaN(modifiedDate.getTime())) { @@ -225,15 +223,55 @@ export class ReportGenerator { } } - // Incorporar a imagem + // Incorporar a imagem com tratamento de erro mais robusto try { let image; + let imageBuffer = data.file.buffer; + + // Se for JPEG, tentar pré-processar com sharp para garantir um formato válido if (data.file.mimetype === 'image/jpeg') { - image = await pdfDoc.embedJpg(data.file.buffer); - } else if (data.file.mimetype === 'image/png') { + try { + // Converter para PNG primeiro - isso resolve muitos problemas de JPEGs corrompidos + const processedBuffer = await sharp(data.file.buffer) + .png() // Converter para PNG que é mais robusto + .toBuffer(); + + // Usar PNG em vez de JPEG para evitar o erro SOI + image = await pdfDoc.embedPng(processedBuffer); + console.log('JPEG convertido para PNG para evitar problemas de formatação'); + } catch (sharpError) { + console.error('Erro ao processar JPEG com sharp:', sharpError); + + // Se falhar, tentar abordagem alternativa + try { + // Tentar usar a versão processada se disponível + if (data.processedFile && data.processedFile.length > 0) { + image = await pdfDoc.embedPng(data.processedFile); + console.log('Usando versão processada da imagem'); + } else { + throw new Error('Imagem processada não disponível'); + } + } catch (fallbackError) { + console.error('Erro no fallback para imagem processada:', fallbackError); + throw new Error('Não foi possível processar a imagem JPEG'); + } + } + } + // Para PNG ou outros formatos + else if (data.file.mimetype === 'image/png') { image = await pdfDoc.embedPng(data.file.buffer); - } else { - image = await pdfDoc.embedPng(data.processedFile); + } + // Para todos os outros tipos, tentar converter para PNG + else { + try { + const pngBuffer = await sharp(data.file.buffer) + .png() + .toBuffer(); + image = await pdfDoc.embedPng(pngBuffer); + } catch (conversionError) { + console.error('Erro ao converter imagem para PNG:', conversionError); + throw new Error('Formato de imagem não suportado'); + } } // Calcular dimensões mantendo proporção @@ -286,13 +324,30 @@ export class ReportGenerator { }); } catch (imageError) { console.error('Erro ao processar imagem:', imageError); + + // Mensagem de erro mais informativa na página page.drawText('Não foi possível exibir a imagem original.', { x: 50, - y: height / 2, + y: height / 2 + 20, size: 14, font: helveticaBold, color: rgb(0.8, 0, 0) }); + + page.drawText(`Erro: ${imageError.message}`, { + x: 50, + y: height / 2 - 10, + size: 12, + font: helvetica, + color: rgb(0.7, 0, 0) + }); + + page.drawText('O documento foi validado, mas a visualização não está disponível.', { + x: 50, + y: height / 2 - 30, + size: 12, + font: helvetica + }); } }