0
# Streaming Email Parsing
1
2
Low-level streaming parser class for advanced email processing scenarios where you need fine-grained control over the parsing process, event handling, and memory usage. Built on Node.js Transform streams for maximum efficiency.
3
4
## Capabilities
5
6
### MailParser Class
7
8
Transform stream class that parses email data and emits events for headers, text content, and attachments as they are processed.
9
10
```javascript { .api }
11
/**
12
* Advanced streaming email parser class
13
* @param options - Parsing configuration options
14
*/
15
class MailParser extends Transform {
16
constructor(options?: ParseOptions);
17
18
/** Parsed email headers (available after 'headers' event) */
19
headers: Map<string, any>;
20
/** Raw header lines (available after 'headers' event) */
21
headerLines: HeaderLine[];
22
/** HTML content (available after parsing completes) */
23
html?: string;
24
/** Plain text content (available after parsing completes) */
25
text?: string;
26
/** Text converted to HTML (available after parsing completes) */
27
textAsHtml?: string;
28
/** Email subject (available after 'headers' event) */
29
subject?: string;
30
/** From address (available after 'headers' event) */
31
from?: AddressObject;
32
/** To addresses (available after 'headers' event) */
33
to?: AddressObject;
34
/** CC addresses (available after 'headers' event) */
35
cc?: AddressObject;
36
/** BCC addresses (available after 'headers' event) */
37
bcc?: AddressObject;
38
/** Email date (available after 'headers' event) */
39
date?: Date;
40
/** Message ID (available after 'headers' event) */
41
messageId?: string;
42
/** List of attachments processed */
43
attachmentList: AttachmentData[];
44
}
45
```
46
47
**Usage Examples:**
48
49
```javascript
50
const { MailParser } = require('mailparser');
51
const fs = require('fs');
52
53
// Basic streaming parsing
54
const parser = new MailParser();
55
56
parser.on('headers', headers => {
57
console.log('Subject:', headers.get('subject'));
58
console.log('From:', headers.get('from'));
59
});
60
61
// Read data from the stream in object mode
62
const readData = () => {
63
let data;
64
while ((data = parser.read()) !== null) {
65
if (data.type === 'text') {
66
console.log('Text content:', data.text);
67
console.log('HTML content:', data.html);
68
}
69
70
if (data.type === 'attachment') {
71
console.log('Attachment:', data.filename);
72
console.log('Size:', data.size);
73
74
// Process attachment content
75
let chunks = [];
76
data.content.on('data', chunk => chunks.push(chunk));
77
data.content.on('end', () => {
78
const content = Buffer.concat(chunks);
79
console.log('Attachment content length:', content.length);
80
data.release(); // Must call to continue processing
81
readData(); // Continue reading after attachment is processed
82
});
83
return; // Wait for attachment to complete before reading more
84
}
85
}
86
};
87
88
parser.on('readable', readData);
89
90
parser.on('end', () => {
91
console.log('Parsing complete');
92
});
93
94
// Pipe email data to parser
95
fs.createReadStream('email.eml').pipe(parser);
96
```
97
98
### Event Handling
99
100
The MailParser class emits several events during the parsing process.
101
102
```javascript { .api }
103
// Headers event - emitted when email headers are parsed
104
on(event: 'headers', listener: (headers: Map<string, any>) => void): this;
105
106
// Header lines event - emitted with raw header data
107
on(event: 'headerLines', listener: (lines: HeaderLine[]) => void): this;
108
109
// Readable event - emitted when data is available to read
110
on(event: 'readable', listener: () => void): this;
111
112
// Stream reading method - call to get text/attachment data
113
read(): TextData | AttachmentData | null;
114
115
// End event - emitted when parsing is complete
116
on(event: 'end', listener: () => void): this;
117
118
// Error event - emitted when parsing errors occur
119
on(event: 'error', listener: (err: Error) => void): this;
120
```
121
122
### Text Data Object
123
124
Returned by the `read()` method when text content is parsed.
125
126
```javascript { .api }
127
interface TextData {
128
/** Always 'text' for text content */
129
type: 'text';
130
/** HTML version of the email content */
131
html?: string;
132
/** Plain text version of the email content */
133
text?: string;
134
/** Plain text converted to HTML format */
135
textAsHtml?: string;
136
}
137
```
138
139
### Attachment Data Object
140
141
Returned by the `read()` method for each attachment found in the email.
142
143
```javascript { .api }
144
interface AttachmentData {
145
/** Always 'attachment' for attachment data */
146
type: 'attachment';
147
/** Readable stream containing attachment content */
148
content: Stream;
149
/** MIME content type of the attachment */
150
contentType: string;
151
/** Part identifier within the email structure */
152
partId?: string;
153
/** Content-Disposition header value */
154
contentDisposition?: string;
155
/** Filename from Content-Disposition or Content-Type */
156
filename?: string;
157
/** Content-ID header value (with angle brackets) */
158
contentId?: string;
159
/** Clean Content-ID without angle brackets */
160
cid?: string;
161
/** Whether attachment is related to email content (embedded) */
162
related?: boolean;
163
/** All headers for this attachment part */
164
headers: Map<string, any>;
165
/** Content checksum (calculated after content is read) */
166
checksum?: string;
167
/** Size in bytes (calculated after content is read) */
168
size?: number;
169
/** Function to call when done processing attachment - REQUIRED */
170
release(): void;
171
}
172
```
173
174
### Advanced Methods
175
176
Additional methods available on the MailParser instance for advanced use cases.
177
178
```javascript { .api }
179
/**
180
* Update image links in HTML content using a custom replacement function
181
* @param replaceCallback - Function to generate replacement URLs
182
* @param done - Callback when processing is complete
183
*/
184
updateImageLinks(
185
replaceCallback: (attachment: AttachmentData, done: (err: Error | null, url?: string) => void) => void,
186
done: (err: Error | null, html?: string) => void
187
): void;
188
189
/**
190
* Convert plain text to HTML with link detection
191
* @param str - Plain text string to convert
192
* @returns HTML formatted string
193
*/
194
textToHtml(str: string): string;
195
196
/**
197
* Format email addresses as HTML
198
* @param addresses - Address objects to format
199
* @returns HTML formatted address string
200
*/
201
getAddressesHTML(addresses: Address[]): string;
202
203
/**
204
* Format email addresses as plain text
205
* @param addresses - Address objects to format
206
* @returns Plain text formatted address string
207
*/
208
getAddressesText(addresses: Address[]): string;
209
```
210
211
### Stream Processing Pattern
212
213
Important pattern for handling attachment streams correctly:
214
215
```javascript
216
parser.on('data', data => {
217
if (data.type === 'attachment') {
218
// Store attachment chunks
219
let chunks = [];
220
let size = 0;
221
222
data.content.on('readable', () => {
223
let chunk;
224
while ((chunk = data.content.read()) !== null) {
225
chunks.push(chunk);
226
size += chunk.length;
227
}
228
});
229
230
data.content.on('end', () => {
231
// Process complete attachment
232
const buffer = Buffer.concat(chunks);
233
console.log(`Processed ${data.filename}: ${buffer.length} bytes`);
234
235
// CRITICAL: Must call release() to continue parsing
236
data.release();
237
});
238
239
data.content.on('error', err => {
240
console.error('Attachment stream error:', err);
241
data.release(); // Release even on error
242
});
243
}
244
});
245
```
246
247
### Memory Management
248
249
For processing large emails efficiently:
250
251
```javascript
252
const parser = new MailParser({
253
checksumAlgo: 'sha256', // Use stronger checksum if needed
254
maxHtmlLengthToParse: 5 * 1024 * 1024 // Limit HTML parsing to 5MB
255
});
256
257
// Handle large attachments by streaming to disk
258
parser.on('data', data => {
259
if (data.type === 'attachment' && data.size > 10 * 1024 * 1024) {
260
// Stream large attachments directly to file system
261
const writeStream = fs.createWriteStream(`/tmp/${data.filename}`);
262
data.content.pipe(writeStream);
263
264
writeStream.on('finish', () => {
265
console.log(`Large attachment saved: ${data.filename}`);
266
data.release();
267
});
268
}
269
});
270
```