61 lines
2.0 KiB
TypeScript
61 lines
2.0 KiB
TypeScript
|
|
import { LoggerProxy as Logger } from 'n8n-workflow';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Extracts filename from Content-Disposition header
|
||
|
|
* @param headers - Response headers object
|
||
|
|
* @returns The extracted filename or null if not found
|
||
|
|
*/
|
||
|
|
export function getFileNameFromHeaders(headers?: { [key: string]: string }): string | null {
|
||
|
|
const who = "Utils::getFileNameFromHeaders:: ";
|
||
|
|
|
||
|
|
if (!headers) {
|
||
|
|
Logger.debug(who + 'No headers provided');
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Look for content-disposition header (case-insensitive)
|
||
|
|
const contentDisposition = Object.keys(headers).find(key =>
|
||
|
|
key.toLowerCase() === 'content-disposition'
|
||
|
|
);
|
||
|
|
|
||
|
|
if (!contentDisposition || !headers[contentDisposition]) {
|
||
|
|
Logger.debug(who + 'Content-Disposition header not found');
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
const headerValue = headers[contentDisposition];
|
||
|
|
Logger.debug(who + 'Found Content-Disposition header', { headerValue });
|
||
|
|
|
||
|
|
// Handle different filename formats in Content-Disposition header
|
||
|
|
// Format 1: filename*=UTF-8''encoded-filename
|
||
|
|
const utf8Match = headerValue.match(/filename\*=UTF-8''([^;]+)/i);
|
||
|
|
if (utf8Match) {
|
||
|
|
try {
|
||
|
|
const decodedFilename = decodeURIComponent(utf8Match[1]);
|
||
|
|
Logger.debug(who + 'Extracted filename from UTF-8 format', { filename: decodedFilename });
|
||
|
|
return decodedFilename;
|
||
|
|
} catch (error) {
|
||
|
|
Logger.error(who + 'Failed to decode UTF-8 filename', { error, encodedFilename: utf8Match[1] });
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Format 2: filename="quoted-filename"
|
||
|
|
const quotedMatch = headerValue.match(/filename="([^"]+)"/i);
|
||
|
|
if (quotedMatch) {
|
||
|
|
Logger.debug(who + 'Extracted filename from quoted format', { filename: quotedMatch[1] });
|
||
|
|
return quotedMatch[1];
|
||
|
|
}
|
||
|
|
|
||
|
|
// Format 3: filename=unquoted-filename
|
||
|
|
const unquotedMatch = headerValue.match(/filename=([^;]+)/i);
|
||
|
|
if (unquotedMatch) {
|
||
|
|
const filename = unquotedMatch[1].trim();
|
||
|
|
Logger.debug(who + 'Extracted filename from unquoted format', { filename });
|
||
|
|
return filename;
|
||
|
|
}
|
||
|
|
|
||
|
|
Logger.debug(who + 'Could not extract filename from Content-Disposition header', { headerValue });
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|