I ended up using a BizFormItemEvents.Insert.After event handler rather than the OnAfterSave event. I think the problem was that the attachment hadn't been fully uploaded until after insert
protected override void OnLoad(EventArgs e)
{
BizFormItemEvents.Insert.After += FormItem_InsertAfterHandler;
base.OnLoad(e);
}
private void FormItem_InsertAfterHandler(object sender, BizFormItemEventArgs e)
{
BizFormItem formDataItem = e.Item;
var myAttachment = (string)formDataItem.GetValue("MyAttachmentFieldName");
// this part needs to be split because the name in DB won't match filesystem
var fullFilePath = $"D:/Web/MyAppFolder/CMS/MyMediaFolder/BizFormFiles/{myAttachment.Split('/')[0]}";
CMS.EmailEngine.EmailMessage em = new CMS.EmailEngine.EmailMessage();
em.EmailFormat = CMS.EmailEngine.EmailFormatEnum.Html;
var attachedFile = new System.Net.Mail.Attachment(fullFilePath);
// then need to set the file name or else it'll be a path
attachedFile.Name = $"{attachment.Split('/')[attachment.Split('/').Length - 1]}";
em.Attachments.Add(attachedFile);
// you also need to assign the other em.From, subject, body etc fields here but that's easy
// then just send it
CMS.EmailEngine.EmailSender.SendEmail(SiteContext.CurrentSiteName, em, true);
}
Then I found out that even if "save to database" is checked for form items they still end up in the file system @ CMS/MyMediaFolderHere/BizFormFiles/~ so I had to add a setting to get the full filepath. Then the attachment can be created from the filepath and added to an email per instructions that can be found online System.Net.Mail.Attachment class