You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Strapi v4生命周期钩子中获取文件并通过Nodemailer发邮件

Strapi v4 生命周期钩子中上传文件附加邮件解决方案

核心问题分析

你的代码存在两个关键问题:

  1. ctx.request.files 是按上传字段名分组的数组结构(如 { attachment: [fileObj] }),直接取 file.name 会报错
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 05:07:40