EML to Excel/CSV Converter
About EML Files & This Tool
EML (Electronic Mail) files are a standard format for storing individual email messages, containing all email data including headers (From, To, Subject, Date), message body, and attachments. This converter tool extracts and preserves all email data without any modification or damage, converting it into Excel (.xlsx) or CSV format for analysis, archiving, or database import.
Data Preview (First 5 emails)
| From |
To |
Subject |
Date |
Message ID |
Body Preview |
transform
Process Files
download
Download Result
How to Open EML Files
Windows
- Microsoft Outlook
- Windows Mail
- Mozilla Thunderbird
- Rename to .mht and open in browser
macOS
- Apple Mail
- Microsoft Outlook for Mac
- Mozilla Thunderbird
- Text editors (raw format)
Linux
- Mozilla Thunderbird
- Evolution Mail
- KMail
- Text editors (gedit, nano)
Android
- Letter Opener app
- EML Reader apps
- Email clients that support EML
- File managers with text view
';
return;
}
fileList.innerHTML = this.files.map((file, index) => `
email
${file.name}
${this.formatFileSize(file.size)}
delete
`).join('');
}
removeFile(index) {
this.files.splice(index, 1);
this.updateFileList();
this.updateButtons();
if (this.files.length === 0) {
this.hidePreview();
document.getElementById('downloadButton').disabled = true;
}
}
updateButtons() {
const processButton = document.getElementById('processButton');
const clearButton = document.getElementById('clearButton');
const hasFiles = this.files.length > 0;
processButton.disabled = !hasFiles;
clearButton.disabled = !hasFiles;
}
async processFiles() {
const progressSection = document.getElementById('progressSection');
const progressBar = document.getElementById('progressBar');
const progressText = document.getElementById('progressText');
progressSection.classList.remove('hidden');
this.processedData = [];
let processedCount = 0;
try {
for (let i = 0; i < this.files.length; i++) {
const file = this.files[i];
progressText.textContent = `Processing ${file.name} (${i + 1}/${this.files.length})`;
progressBar.value = (i / this.files.length);
try {
const emailData = await this.parseEMLFile(file);
this.processedData.push(emailData);
processedCount++;
} catch (error) {
console.error(`Error processing ${file.name}:`, error);
this.showError(`Error processing ${file.name}: ${error.message}`);
}
// Small delay for UI responsiveness
await new Promise(resolve => setTimeout(resolve, 50));
}
progressBar.value = 1;
progressText.textContent = `Processing complete! Successfully processed ${processedCount}/${this.files.length} files.`;
setTimeout(() => {
progressSection.classList.add('hidden');
if (this.processedData.length > 0) {
this.showPreview();
document.getElementById('downloadButton').disabled = false;
this.showSuccess(`Successfully processed ${processedCount} EML files. Data is ready for download.`);
} else {
this.showError('No files were successfully processed. Please check your EML files and try again.');
}
}, 1000);
} catch (error) {
progressSection.classList.add('hidden');
this.showError('An error occurred during processing. Please try again.');
console.error('Processing error:', error);
}
}
async parseEMLFile(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => {
try {
const emlContent = e.target.result;
const parsed = this.parseEMLContent(emlContent);
resolve({
filename: file.name,
fileSize: this.formatFileSize(file.size),
...parsed
});
} catch (error) {
reject(error);
}
};
reader.onerror = () => reject(new Error('Failed to read file'));
reader.readAsText(file, 'utf-8');
});
}
parseEMLContent(content) {
const headers = {};
const lines = content.split(/\r?\n/);
let headerSection = true;
let bodyLines = [];
let currentHeader = null;
for (let line of lines) {
if (headerSection) {
if (line.trim() === '') {
headerSection = false;
continue;
}
if (line.match(/^\s/)) {
// Continuation of previous header
if (currentHeader && headers[currentHeader]) {
headers[currentHeader] += ' ' + line.trim();
}
} else {
const colonIndex = line.indexOf(':');
if (colonIndex > 0) {
currentHeader = line.substring(0, colonIndex).toLowerCase().trim();
headers[currentHeader] = line.substring(colonIndex + 1).trim();
}
}
} else {
bodyLines.push(line);
}
}
const body = bodyLines.join('\n');
const plainTextBody = this.extractPlainText(body, headers['content-type'] || '');
return {
from: this.decodeHeader(headers.from || ''),
to: this.decodeHeader(headers.to || ''),
cc: this.decodeHeader(headers.cc || ''),
bcc: this.decodeHeader(headers.bcc || ''),
subject: this.decodeHeader(headers.subject || ''),
date: this.decodeHeader(headers.date || ''),
messageId: headers['message-id'] || '',
contentType: headers['content-type'] || '',
xMailer: headers['x-mailer'] || '',
returnPath: headers['return-path'] || '',
received: headers['received'] || '',
mimeVersion: headers['mime-version'] || '',
body: plainTextBody,
bodyPreview: plainTextBody.substring(0, 150).replace(/\s+/g, ' ').trim() + (plainTextBody.length > 150 ? '...' : ''),
rawHeaders: Object.keys(headers).map(key => `${key}: ${headers[key]}`).join('\n'),
fullContent: content
};
}
decodeHeader(header) {
if (!header) return '';
// Decode RFC 2047 encoded-words
return header.replace(/=\?([^?]+)\?([BQ])\?([^?]*)\?=/gi, (match, charset, encoding, encoded) => {
try {
if (encoding.toLowerCase() === 'b') {
return atob(encoded);
} else if (encoding.toLowerCase() === 'q') {
return encoded.replace(/[=]([0-9A-F]{2})/gi, (m, hex) =>
String.fromCharCode(parseInt(hex, 16))
).replace(/_/g, ' ');
}
} catch (e) {
console.warn('Header decode error:', e);
}
return match;
}).replace(/\s+/g, ' ').trim();
}
extractPlainText(body, contentType) {
if (!body) return '';
// Handle quoted-printable encoding
body = body.replace(/=\r?\n/g, '').replace(/=([0-9A-F]{2})/gi, (match, hex) => {
try {
return String.fromCharCode(parseInt(hex, 16));
} catch (e) {
return match;
}
});
// Handle HTML content
if (contentType.toLowerCase().includes('text/html')) {
return body
.replace(/