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

C#邮件附件功能优化:如何支持所有文件类型而非仅TXT?

解决邮件附件支持所有文件类型的问题

你的代码里用StreamReader(文本模式)读取所有附件,这会破坏二进制文件的字节结构,导致压缩包、图片这类文件损坏。要支持所有文件类型,得区分文本文件和二进制文件分别处理:

核心修改思路

  • 文本文件:保留内容替换逻辑,同时注意编码兼容性
  • 二进制文件:直接复制原文件到临时路径,不做内容替换(替换二进制内容会彻底破坏文件)
  • 优化临时文件处理:原来的代码添加附件后立刻删除临时文件,可能导致MimeKit读取失败,建议发送邮件完成后再统一清理

修改后的完整代码

public async Task<ResponseData> SendEmail(VictimData victimData, SmtpData senderData, LetterData letter)
{
    ResponseData response = new ResponseData();
    BodyBuilder bodyBuilder = new BodyBuilder();
    List<string> tempFiles = new List<string>(); // 保存临时文件路径,发送完统一删除
    try
    {
        MimeMessage message = new MimeMessage();
        message.From.Add(new MailboxAddress(senderData.senderName, senderData.senderEmail));
        message.To.Add(new MailboxAddress("", victimData.email));
        message.Subject = letter.subject;

        if (letter.isHtml)
        {
            bodyBuilder.HtmlBody = letter.body;
        }
        else
        {
            bodyBuilder.TextBody = letter.body;
        }

        foreach (var attachment in letter.attachment)
        {
            string tempFile = ProcessAttachment(attachment.filePath, victimData.email, attachment.fileName);
            tempFiles.Add(tempFile);
            bodyBuilder.Attachments.Add(tempFile);
        }

        message.Body = bodyBuilder.ToMessageBody();
        await client.SendAsync(message);
        
        // 发送完成后删除所有临时文件
        foreach (var tempFile in tempFiles)
        {
            if (File.Exists(tempFile))
            {
                File.Delete(tempFile);
                Console.WriteLine($"已删除临时文件:{tempFile}");
            }
        }

        response.status = true;
        response.messages = "";
        return response;
    }
    catch (Exception ex)
    {
        // 异常时也清理临时文件
        foreach (var tempFile in tempFiles)
        {
            if (File.Exists(tempFile)) File.Delete(tempFile);
        }
        response.messages = ex.Message;
        response.status = false;
        return response;
    }
}

private string ProcessAttachment(string filePath, string email, string newFileName)
{
    Replacer replacer = new Replacer();
    string tempFile = Path.Combine(Helpers.tempPath, replacer.DoReplace(newFileName, email));

    // 判断文件是否为文本文件(通过检测非打印字符比例)
    if (IsTextFile(filePath))
    {
        // 文本文件:读取并替换内容
        string contentResult = File.ReadAllText(filePath);
        contentResult = replacer.DoReplace(contentResult, email);
        File.WriteAllText(tempFile, contentResult);
    }
    else
    {
        // 二进制文件:直接复制原文件
        File.Copy(filePath, tempFile, overwrite: true);
    }

    return tempFile;
}

// 简单判断是否为文本文件的方法
private bool IsTextFile(string filePath)
{
    const int sampleSize = 1024;
    byte[] buffer = new byte[sampleSize];
    int bytesRead = 0;

    using (FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
    {
        bytesRead = stream.Read(buffer, 0, sampleSize);
    }

    // 统计非打印字符占比,超过10%判定为二进制文件
    int nonPrintableChars = 0;
    foreach (byte b in buffer.Take(bytesRead))
    {
        // 排除换行、回车、制表符这些合法文本控制字符
        if (b < 32 && b != 9 && b != 10 && b != 13)
        {
            nonPrintableChars++;
        }
    }

    return (nonPrintableChars / (double)bytesRead) <= 0.1;
}

关键修改点说明

  1. 新增文件类型判断:IsTextFile方法通过检测文件中的非打印字符比例,区分文本和二进制文件,避免破坏二进制文件结构
  2. 分离处理逻辑:文本文件继续执行内容替换,二进制文件直接复制原文件
  3. 优化临时文件管理:用列表保存所有临时文件路径,等邮件发送完成(或异常时)统一删除,避免MimeKit读取临时文件时文件已被删除
  4. 简化文本读写:用File.ReadAllText和File.WriteAllText替代StreamReader/StreamWriter,代码更简洁,同时自动处理编码

内容的提问来源于stack exchange,提问作者Олег

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 14:36:35