fix(media): deriva extensão/content-type do mimetype (corrige arquivos .bin)
Documentos sem nome/extensão eram salvos como .bin mesmo tendo mimetype conhecido. Agora a extensão vem de: nome original → mimetype (pdf/docx/xlsx/ mp3/...) → .bin só como último recurso. O serve do Wasabi (/api/storage/view) também passa a mapear content-type para muitos tipos, não só octet-stream. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+45
-2
@@ -913,8 +913,12 @@ export class MessageHandler {
|
||||
const originalMimetype = docMsg?.mimetype || this.mimetypeForType(contentType)
|
||||
|
||||
let ext: string
|
||||
if (contentType === 'documentMessage' && originalFileName) {
|
||||
ext = path.extname(originalFileName).replace('.', '') || 'bin'
|
||||
if (contentType === 'documentMessage') {
|
||||
// Ordem: 1) extensão do nome original; 2) derivada do mimetype real do
|
||||
// documento; 3) só então .bin. Antes ia direto p/ .bin quando o doc não
|
||||
// tinha nome/extensão — mesmo tendo mimetype conhecido (pdf, docx…).
|
||||
const fromName = originalFileName ? path.extname(originalFileName).replace('.', '') : ''
|
||||
ext = fromName || this.extFromMimetype(originalMimetype) || 'bin'
|
||||
} else {
|
||||
ext = this.extensionForType(contentType)
|
||||
}
|
||||
@@ -1107,6 +1111,45 @@ export class MessageHandler {
|
||||
return map[contentType] ?? 'bin'
|
||||
}
|
||||
|
||||
/** Deriva a extensão de arquivo a partir do mimetype (documentos variados). */
|
||||
private extFromMimetype(mimetype?: string | null): string | null {
|
||||
if (!mimetype) return null
|
||||
const clean = mimetype.split(';')[0].trim().toLowerCase()
|
||||
const map: Record<string, string> = {
|
||||
'application/pdf': 'pdf',
|
||||
'application/msword': 'doc',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx',
|
||||
'application/vnd.ms-excel': 'xls',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx',
|
||||
'application/vnd.ms-powerpoint': 'ppt',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'pptx',
|
||||
'application/zip': 'zip',
|
||||
'application/x-rar-compressed': 'rar',
|
||||
'application/vnd.rar': 'rar',
|
||||
'application/x-7z-compressed': '7z',
|
||||
'application/gzip': 'gz',
|
||||
'text/plain': 'txt',
|
||||
'text/csv': 'csv',
|
||||
'text/html': 'html',
|
||||
'application/json': 'json',
|
||||
'application/xml': 'xml',
|
||||
'image/jpeg': 'jpg',
|
||||
'image/png': 'png',
|
||||
'image/gif': 'gif',
|
||||
'image/webp': 'webp',
|
||||
'video/mp4': 'mp4',
|
||||
'video/3gpp': '3gp',
|
||||
'video/quicktime': 'mov',
|
||||
'audio/ogg': 'ogg',
|
||||
'audio/mpeg': 'mp3',
|
||||
'audio/mp4': 'm4a',
|
||||
'audio/amr': 'amr',
|
||||
'audio/aac': 'aac',
|
||||
'audio/wav': 'wav',
|
||||
}
|
||||
return map[clean] ?? null
|
||||
}
|
||||
|
||||
/** Retorna o mimetype adequado para o tipo de mídia */
|
||||
private mimetypeForType(contentType: string): string {
|
||||
const map: Record<string, string> = {
|
||||
|
||||
@@ -126,13 +126,20 @@ async function bootstrap() {
|
||||
const buffer = await storageProvider.getBuffer(filePath)
|
||||
if (!buffer) { res.status(404).end(); return }
|
||||
const ext = filePath.split('.').pop()?.toLowerCase()
|
||||
const mime =
|
||||
ext === 'webp' ? 'image/webp' :
|
||||
ext === 'jpg' || ext === 'jpeg' ? 'image/jpeg' :
|
||||
ext === 'png' ? 'image/png' :
|
||||
ext === 'mp4' ? 'video/mp4' :
|
||||
ext === 'ogg' ? 'audio/ogg' :
|
||||
'application/octet-stream'
|
||||
const MIME_BY_EXT: Record<string, string> = {
|
||||
jpg: 'image/jpeg', jpeg: 'image/jpeg', png: 'image/png', gif: 'image/gif', webp: 'image/webp',
|
||||
mp4: 'video/mp4', '3gp': 'video/3gpp', mov: 'video/quicktime',
|
||||
ogg: 'audio/ogg', mp3: 'audio/mpeg', m4a: 'audio/mp4', amr: 'audio/amr', aac: 'audio/aac', wav: 'audio/wav',
|
||||
pdf: 'application/pdf', doc: 'application/msword',
|
||||
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
xls: 'application/vnd.ms-excel',
|
||||
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
ppt: 'application/vnd.ms-powerpoint',
|
||||
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
zip: 'application/zip', rar: 'application/vnd.rar', '7z': 'application/x-7z-compressed', gz: 'application/gzip',
|
||||
txt: 'text/plain', csv: 'text/csv', json: 'application/json', xml: 'application/xml', html: 'text/html',
|
||||
}
|
||||
const mime = (ext && MIME_BY_EXT[ext]) ?? 'application/octet-stream'
|
||||
res.setHeader('Content-Type', mime)
|
||||
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable')
|
||||
res.send(buffer)
|
||||
|
||||
Reference in New Issue
Block a user