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

调用Microsoft Graph API发送邮件遇HttpWebRequest 413错误

问题:使用Microsoft Graph API发送带大附件邮件时返回Error 413
  • 环境:C# WPF应用,调用Microsoft Graph API的/me/sendMail接口
  • 现象:发送单个100KB附件正常,发送三个附件(2MB PDF + 20MB PDF + 100KB CSV)时返回413错误(请求实体过大)
  • 无效尝试:设置内容长度、启用SendChunked;找到的解决方案多涉及WCF和修改web.config,但应用无此配置文件
  • 疑问:Graph文档标注附件总大小上限为150MB,当前总大小远低于该值,寻求适配WPF的有效方案

原发送代码

public void SendEmail(string messageJson, string token)
{
    HttpWebRequest request = WebRequest.Create($"https://graph.microsoft.com/v1.0/me/sendMail") as HttpWebRequest;
    request.ContentType = "application/json";
    request.Method = "POST";
    request.Accept = ("application/json");
    request.Headers.Add("Authorization", $"Bearer {token}");

    string resultJSON = string.Empty;
    try
    {
        //Send Request
        using (var streamWriter = new StreamWriter(request.GetRequestStream()))
        {
            streamWriter.Write(messageJson);
        }

        using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
        {
            using (StreamReader reader = new StreamReader(response.GetResponseStream()))
            {
                resultJSON = reader.ReadToEnd();
            }
        }
    }
    catch (WebException e)
    {
        var response = ((HttpWebResponse)e.Response);
        var someheader = response.Headers["X-API-ERROR"];
        // check header

        string content = string.Empty;

        // protocol errors find the statuscode in the Response
        // the enum statuscode can be cast to an int.
        int code = (int)((HttpWebResponse)e.Response).StatusCode;
        using (var reader = new StreamReader(e.Response.GetResponseStream()))
        {
            content = reader.ReadToEnd();
        }

        Console.WriteLine(content);
    }
}

解决方案:使用Graph API上传会话分块处理大附件

核心原因

直接将大附件以base64编码放入sendMail的JSON请求体中,会导致单个请求的实体体积过大,触发413错误。Graph的150MB是附件总大小上限,但单请求的payload有单独限制,因此需要用分块上传的方式先上传附件,再关联到邮件发送。

具体实现步骤

  1. 创建草稿邮件(不含大附件):先调用POST /me/messages创建仅包含主题、收件人、正文的草稿邮件,暂不添加大附件。
  2. 为大附件创建上传会话:对20MB这类大附件,调用POST /me/messages/{messageId}/attachments/createUploadSession接口,获取上传会话URL。
  3. 分块上传附件内容:使用上传会话URL,将大附件按5MB-10MB的块大小分块上传(Graph推荐范围)。
  4. 添加小附件到草稿:对100KB CSV、2MB PDF这类小附件,直接调用POST /me/messages/{messageId}/attachments接口添加。
  5. 发送草稿邮件:调用POST /me/messages/{messageId}/send发送已添加全附件的草稿。

关键代码片段

创建上传会话

public string CreateAttachmentUploadSession(string messageId, string attachmentName, long fileSize, string token)
{
    var uploadSessionRequest = new
    {
        AttachmentItem = new
        {
            AttachmentType = "file",
            Name = attachmentName,
            Size = fileSize
        }
    };

    var jsonContent = Newtonsoft.Json.JsonConvert.SerializeObject(uploadSessionRequest);
    var request = WebRequest.Create($"https://graph.microsoft.com/v1.0/me/messages/{messageId}/attachments/createUploadSession") as HttpWebRequest;
    request.ContentType = "application/json";
    request.Method = "POST";
    request.Headers.Add("Authorization", $"Bearer {token}");

    using (var streamWriter = new StreamWriter(request.GetRequestStream()))
    {
        streamWriter.Write(jsonContent);
    }

    using (var response = (HttpWebResponse)request.GetResponse())
    using (var reader = new StreamReader(response.GetResponseStream()))
    {
        var sessionJson = reader.ReadToEnd();
        var sessionObj = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(sessionJson);
        return sessionObj.uploadUrl;
    }
}

分块上传文件

public void UploadFileInChunks(string uploadUrl, string filePath, int chunkSize = 5 * 1024 * 1024)
{
    var fileInfo = new FileInfo(filePath);
    long fileSize = fileInfo.Length;
    long bytesUploaded = 0;

    using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
    {
        while (bytesUploaded < fileSize)
        {
            long bytesToRead = Math.Min(chunkSize, fileSize - bytesUploaded);
            var buffer = new byte[bytesToRead];
            fileStream.Read(buffer, 0, (int)bytesToRead);

            var request = WebRequest.Create(uploadUrl) as HttpWebRequest;
            request.Method = "PUT";
            request.ContentLength = bytesToRead;
            request.Headers.Add("Content-Range", $"bytes {bytesUploaded}-{bytesUploaded + bytesToRead - 1}/{fileSize}");

            using (var requestStream = request.GetRequestStream())
            {
                requestStream.Write(buffer, 0, buffer.Length);
            }

            using (var response = (HttpWebResponse)request.GetResponse())
            {
                if (response.StatusCode == HttpStatusCode.Created)
                {
                    // 上传完成
                    break;
                }
                else if (response.StatusCode != HttpStatusCode.Accepted)
                {
                    throw new Exception($"上传分块失败,状态码:{(int)response.StatusCode}");
                }
            }

            bytesUploaded += bytesToRead;
        }
    }
}

注意事项

  • 分块大小建议设置为5MB-10MB,避免过小导致请求过多,过大触发单块限制
  • 上传会话默认有效期24小时,需在此时间内完成上传
  • 小附件可直接添加,无需分块,简化流程

内容的提问来源于stack exchange,提问作者cWilson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 05:50:30