我正在寻找一种使用 Node js 以编程方式检查电子邮件是否为有效电子邮件的方法。经过一些研究后,我最终找到了这个 YouTube 视频。
现在看完视频后,我有了这个代码:
const { randomBytes } = require('crypto');
const promises = require('dns').promises;
const net = require('net');
const resolveMXRecords = async (domain) => {
console.log('resolving mx records for domain...', domain)
try {
return await promises.resolveMx(domain)
} catch (error) {
return []
}
}
const verifyEmailFormat = (email) => {
const regex = new RegExp({ pattern: '^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,4}$', flags: 'i' });
return regex.test(email);
};
const SMTPStages = {
CHECK_CONNECTION_ESTABLISHED: 'CHECK_CONNECTION_ESTABLISHED',
SEND_EHLO: 'SEND_EHLO',
SEND_MAIL_FROM: 'SEND_MAIL_FROM',
SEND_RECIPIENT_TO: 'SEND_RECIPIENT_TO',
SEND_QUIT: 'SEND_QUIT'
}
const testInboxOnSMTPServer = async (inboxAddress, smtpAddress) => {
return new Promise((resolve, reject) => {
const result = {
connection_established: false,
account_exists: false
}
const socket = net.createConnection(25, smtpAddress)
const stages = {
[SMTPStages.CHECK_CONNECTION_ESTABLISHED]: {
expected_reply_code: '220'
},
[SMTPStages.SEND_EHLO]: {
command: `EHLO mail.example.org\r\n`,
expected_reply_code: '250'
},
[SMTPStages.SEND_MAIL_FROM]: {
command: `MAIL FROM:<[email protected]>\r\n`,
expected_reply_code: '250'
},
[SMTPStages.SEND_RECIPIENT_TO]: {
command: `RCPT TO:<${inboxAddress}>\r\n`,
expected_reply_code: '250'
},
[SMTPStages.SEND_QUIT]: {
command: `QUIT\r\n`,
expected_reply_code: '221'
}
}
let response = ""
let currentStageName = SMTPStages.CHECK_CONNECTION_ESTABLISHED
socket.on("data", (data) => {
const reply = data.toString()
response += reply
// console.log('<-- ' + reply)
const currentStage = stages[currentStageName]
switch (currentStageName) {
case SMTPStages.CHECK_CONNECTION_ESTABLISHED: {
if (!reply.startsWith(currentStage.expected_reply_code)) {
socket.end()
break
}
result.connection_established = true
currentStageName = SMTPStages.SEND_EHLO
const command = stages[currentStageName].command
socket.write(command, () => {
console.log('--> ' + command)
})
break
}
case SMTPStages.SEND_EHLO: {
if (!reply.startsWith(currentStage.expected_reply_code)) {
socket.end()
break
}
currentStageName = SMTPStages.SEND_MAIL_FROM
const command = stages[currentStageName].command
socket.write(command, () => {
console.log('--> ' + command)
})
break
}
case SMTPStages.SEND_MAIL_FROM: {
if (!reply.startsWith(currentStage.expected_reply_code)) {
socket.end()
break
}
currentStageName = SMTPStages.SEND_RECIPIENT_TO
const command = stages[currentStageName].command
socket.write(command, () => {
console.log('--> ' + command)
})
break
}
case SMTPStages.SEND_RECIPIENT_TO: {
if (!reply.startsWith(currentStage.expected_reply_code)) {
socket.end()
break
}
result.account_exists = true
currentStageName = SMTPStages.SEND_QUIT
const command = stages[currentStageName].command
socket.write(command, () => {
console.log('--> ' + command)
})
break
}
}
})
socket.on("connect", () => {
console.log("Connected to SMTP " + smtpAddress)
console.log('Local address: ' + socket.localAddress + ' Local port: ' + socket.localPort);
console.log('Remote address: ' + socket.remoteAddress + ' Remote port: ' + socket.remotePort);
})
socket.on("error", (error) => {
// clearInterval(timeoutTimer)
reject(error)
})
socket.on("close", () => {
// clearInterval(timeoutTimer)
resolve(result)
})
})
}
const validateEmail = async (email) => {
const emailFormatValid = verifyEmailFormat(email)
if (!emailFormatValid) {
return 'invalid email format'
}
const [, domain] = email.split('@')
const mxRecords = await resolveMXRecords(domain)
const sortedMxRecords = mxRecords.sort((a, b) => a.priority - b.priority)
console.log('sortedMxRecords', sortedMxRecords)
let smtpResult = { connection_established: false, account_exists: false }
let hostIndex = 0
while (hostIndex < sortedMxRecords.length) {
try {
console.log('testing inbox on smtp server', sortedMxRecords[hostIndex].exchange)
smtpResult = await testInboxOnSMTPServer(email, sortedMxRecords[hostIndex].exchange)
if (!smtpResult.connection_established) {
console.log('connection not established')
hostIndex++
}
else {
break
}
} catch (error) {
console.error('error while testing inbox on smtp server', error)
hostIndex++
}
}
let usesCatchAll = false
try {
const testCatchEmail = `${randomBytes(20).toString('hex')}@${domain}`
const testCatchAll = await testInboxOnSMTPServer(testCatchEmail, sortedMxRecords[hostIndex].exchange)
usesCatchAll = testCatchAll.account_exists
} catch (error) {
console.error(error)
}
return {
email_format_is_valid: emailFormatValid,
usesCatchAll: usesCatchAll,
...smtpResult
}
}
async function main() {
let email = '[email protected]'
const result = await validateEmail(email)
console.log('result', result)
}
main()
此代码在大多数情况下都有效。当我尝试运行它时,它给出了一些准确的结果。但我面临一些问题,第一个是当我尝试在远程 Linux 服务器上运行相同的代码时,它不断地给我一个连接错误。其次,我想检查一个非常大的电子邮件列表(大约 400 万封)。如何实现这一目标而不被服务器阻止或禁止?如何处理某些电子邮件的连接问题?任何帮助将不胜感激。
让我们看看另一个变体来做到这一点,我认为它更容易。
npm install deep-email-validator
const router = express.Router();
const emailValidator = require('deep-email-validator');
async function isEmailValid(email) {
return emailValidator.validate(email)
}
valid: false,
validators: {
regex: { valid: true },
typo: { valid: true },
disposable: { valid: true },
mx: { valid: true },
smtp: { valid: false, reason: 'Mailbox not found.' }
},
reason: 'smtp'
}
您可以在此处
阅读有关此验证的更多信息