Serverless Node.js中REST API转Multipart文件为邮件附件的实现咨询
Your core idea is solid, but the current code misses critical functionality (handling multipart/form-data, processing the uploaded file, and validating the PATCH method) and has some areas where we can boost security, readability, and robustness. Here's a refined approach:
Key Improvements & Fixes
1. Parse Multipart/Form-Data Requests
Lambda's event payload doesn't automatically parse multipart data—you'll need a helper library to extract the uploaded file. lambda-multipart-parser is a great fit for this use case (it's lightweight and designed specifically for Lambda).
2. Secure Configuration with Environment Variables
Never hardcode credentials in your code! Store SMTP credentials, hosts, and ports as Lambda environment variables—this keeps sensitive data safe and makes configuration changes easier without redeploying code.
3. Use Async/Await Instead of Callbacks
Callback-based code can get messy quickly. Switching to async/await makes your code more readable and simplifies error handling.
4. Validate Request Method & Input
Ensure the request uses the PATCH method, and check that a file was actually uploaded before attempting to send the email.
5. Properly Attach the Uploaded File to the Email
Once you extract the file from the request, add it to Nodemailer's attachments array with the correct metadata (filename, content type, and content).
Refactored Code Example
First, install the required dependencies:
npm install nodemailer lambda-multipart-parser
Then update your code:
const nodemailer = require('nodemailer'); const multipartParser = require('lambda-multipart-parser'); // Pull config from Lambda environment variables const config = { smtp: { host: process.env.SMTP_HOST, port: parseInt(process.env.SMTP_PORT, 10), username: process.env.SMTP_USERNAME, password: process.env.SMTP_PASSWORD }, email: { from: process.env.EMAIL_FROM || 'wat@address.com', to: process.env.EMAIL_TO || 'inbox@address.com' } }; // Reuse transporter across invocations (helps with Lambda cold starts) let transporter; const getTransporter = () => { if (!transporter) { transporter = nodemailer.createTransport({ host: config.smtp.host, port: config.smtp.port, secure: config.smtp.port === 465, // Auto-enable secure for port 465 auth: { user: config.smtp.username, pass: config.smtp.password } }); } return transporter; }; module.exports.endpoint = async (event) => { try { // Validate request method if (event.httpMethod !== 'PATCH') { return { statusCode: 405, headers: { 'Access-Control-Allow-Origin': process.env.CORS_ORIGIN || '*' }, body: JSON.stringify({ error: 'Method Not Allowed. Use PATCH.' }) }; } // Parse multipart form data const parsedData = await multipartParser.parse(event); // Check if a file was uploaded if (!parsedData.files || parsedData.files.length === 0) { return { statusCode: 400, headers: { 'Access-Control-Allow-Origin': process.env.CORS_ORIGIN || '*' }, body: JSON.stringify({ error: 'No file uploaded. Please attach a file.' }) }; } // Prepare email options with attachment const mailOptions = { from: config.email.from, to: config.email.to, subject: 'Here is a file', text: 'Please see attached', attachments: parsedData.files.map(file => ({ filename: file.filename, content: file.content, contentType: file.contentType })) }; // Send email await getTransporter().sendMail(mailOptions); return { statusCode: 202, headers: { 'Access-Control-Allow-Origin': process.env.CORS_ORIGIN || '*' }, body: JSON.stringify({ message: 'Accepted. File will be sent via email.' }) }; } catch (error) { console.error('Error processing request:', error); return { statusCode: 500, headers: { 'Access-Control-Allow-Origin': process.env.CORS_ORIGIN || '*' }, body: JSON.stringify({ error: `Failed to process request: ${error.message}` }) }; } };
Additional Recommendations
- Restrict CORS: Instead of using
*forAccess-Control-Allow-Origin, set it to specific domains (e.g.,https://yourfrontend.com) to improve security. - File Validation: Add checks for file size (e.g., reject files larger than 10MB) and allowed file types (e.g., only PDFs, images) to prevent abuse.
- Logging: Use structured logging (e.g.,
console.log(JSON.stringify({ event: 'email_sent', timestamp: new Date().toISOString() }))) to make debugging easier. - SMTP Connection Testing: Add a quick connection test when initializing the transporter to catch configuration errors early.
内容的提问来源于stack exchange,提问作者Matt

