如何在Strapi v4生命周期钩子中获取文件并通过Nodemailer发邮件
Strapi v4 生命周期钩子中上传文件附加邮件解决方案
核心问题分析
你的代码存在两个关键问题:
ctx.request.files是按上传字段名分组的数组结构(如{ attachment: [fileObj] }),直接取file.name会报错- beforeCreate 阶段文件仅存于临时目录,若邮件发送延迟或Strapi提前清理临时文件会导致附件失效;更可靠的方式是在
afterCreate中处理已持久化的文件
方案1:BeforeCreate 处理临时上传文件
适合需要在内容创建前发送邮件的场景,需直接读取临时上传文件:
const { ApplicationError } = require('@strapi/utils').errors; beforeCreate: async (event) => { const ctx = strapi.requestContext.get(); // multipart/form-data 请求下,ctx.request.body.data 已是解析后的对象,无需JSON.parse const { full_name, email } = ctx.request.body.data; // 假设上传字段名为 attachment,取第一个文件 const uploadField = 'attachment'; const fileArr = ctx.request.files?.[uploadField]; if (!fileArr || fileArr.length === 0) { throw new ApplicationError('No file uploaded'); } const file = fileArr[0]; const trackingCode = await strapi.config.helperFunction.generateRandomItn(event); const subject = `Your Tracking Code: ${trackingCode}`; const content = `<p>Hi ${full_name}, your submission is received. Tracking code: ${trackingCode}</p>`; try { const emailOptions = { to: email, subject: subject, html: content, attachments: [ { filename: file.name, // 直接使用临时文件的绝对路径 path: file.path, contentType: file.type }, ], }; await strapi.plugins['email'].services.email.send(emailOptions); strapi.log.debug(`Email sent to ${email} with attachment ${file.name}`); } catch (err) { strapi.log.error(`Failed to send email`, err); throw new ApplicationError(`Email sending failed: ${err.message}`); } },
方案2:AfterCreate 处理已持久化文件(推荐)
文件已被Strapi存入永久目录(默认public/uploads),通过内容实体的关联字段获取文件信息:
前提:
在你的内容类型中添加一个媒体字段(如命名为attachment),关联到Strapi的upload.file集合
const { ApplicationError } = require('@strapi/utils').errors; afterCreate: async (event) => { // 获取创建后的内容实体,关联查询上传文件 const entry = await strapi.db.query('api::your-content-type.your-content-type').findOne({ where: { id: event.result.id }, populate: ['attachment'] // 替换为你的媒体字段名 }); const { full_name, email, attachment } = entry; if (!attachment) { strapi.log.debug(`No attachment found for entry ${entry.id}, skipping email`); return; } const trackingCode = await strapi.config.helperFunction.generateRandomItn(event); const subject = `Your Tracking Code: ${trackingCode}`; const content = `<p>Hi ${full_name}, your submission is received. Tracking code: ${trackingCode}</p>`; try { // 构造文件绝对路径:Strapi存储的文件URL相对public目录 const filePath = `${process.cwd()}/public${attachment.url}`; const emailOptions = { to: email, subject: subject, html: content, attachments: [ { filename: attachment.name, path: filePath, contentType: attachment.mime }, ], }; await strapi.plugins['email'].services.email.send(emailOptions); strapi.log.debug(`Email sent to ${email} with attachment ${attachment.name}`); } catch (err) { strapi.log.error(`Failed to send email`, err); throw new ApplicationError(`Email sending failed: ${err.message}`); } },
关键注意事项
- 邮件插件配置:确保Strapi的
email插件已正确配置Nodemailer - 多文件处理:若支持多文件上传,需循环
fileArr或entry.attachments数组添加多个附件 - 权限检查:确保Strapi运行进程拥有
public/uploads目录的读取权限 - 错误兜底:添加文件存在性检查,避免因文件缺失导致邮件发送失败
内容的提问来源于stack exchange,提问作者Mark Marton
相关产品推荐
相关产品推荐

