SendGrid Attachments Are Empty or Corrupt Using API (VBA)

前端 未结 3 455
日久生厌
日久生厌 2021-01-21 13:44

This seems to be a constant issue with the SendGrid Web API and emailing attachments. I\'ve found many, many posts across the web all of whom are having this same issue... but n

相关标签:
3条回答
  • 2021-01-21 14:08

    Here it is!

    Option Explicit
    
    Sub SendEmailUsingSendGrid()
        Dim attachmentPath As String: attachmentPath = "C:\temp\test.png"
        Dim HttpReqURL As String: HttpReqURL = "https://api.sendgrid.com/api/mail.send.json"
    
        Const adSaveCreateNotExist = 1
        Const adSaveCreateOverWrite = 2
        Const adTypeBinary = 1
        Const adTypeText = 2
        Const adModeReadWrite = 3
    
        Dim YOUR_SG_CREDS_USERNAME As String
        YOUR_SG_CREDS_USERNAME = "username"
    
        Dim YOUR_SG_CREDS_PASSWORD As String
        YOUR_SG_CREDS_PASSWORD = "password"
    
        Dim multiPartUploadBoundary As String
        multiPartUploadBoundary = "123456789abc"
    
        Dim eTo As String
        eTo = "to@example.com"
    
        Dim eToName As String
        eToName = "To Name"
    
        Dim eSubject As String
        eSubject = "My Subject"
    
        Dim eBody As String
        eBody = "This is a test!"
    
        Dim eFrom As String
        eFrom = "from@example.com"
    
        Dim outputStream As Object
        Set outputStream = CreateObject("adodb.stream")
        outputStream.Type = adTypeText
        outputStream.Mode = adModeReadWrite
        outputStream.charset = "windows-1252"
        outputStream.Open
    
        AddParameterAndValueToStream outputStream, multiPartUploadBoundary, "api_user", YOUR_SG_CREDS_USERNAME
        AddParameterAndValueToStream outputStream, multiPartUploadBoundary, "api_key", YOUR_SG_CREDS_PASSWORD
        AddParameterAndValueToStream outputStream, multiPartUploadBoundary, "to", eTo
        AddParameterAndValueToStream outputStream, multiPartUploadBoundary, "toname", eToName
        AddParameterAndValueToStream outputStream, multiPartUploadBoundary, "subject", eSubject
        AddParameterAndValueToStream outputStream, multiPartUploadBoundary, "text", eBody
        AddParameterAndValueToStream outputStream, multiPartUploadBoundary, "from", eFrom
        AddFileToStream outputStream, multiPartUploadBoundary, "test.png", "C:\temp\test.png"
        outputStream.WriteText "--" + multiPartUploadBoundary + "--" + vbCrLf
    
        Dim binaryStream As Object
        Set binaryStream = CreateObject("ADODB.Stream")
        binaryStream.Mode = 3 'read write
        binaryStream.Type = 1 'adTypeText 'Binary
        binaryStream.Open
    
        ' copy text to binary stream so xmlHttp.send works correctly
        outputStream.Position = 0
        outputStream.CopyTo binaryStream
        outputStream.Close
    
        binaryStream.Position = 0
    
        Dim xmlHttp As Object
        Set xmlHttp = CreateObject("MSXML2.XMLHTTP")
        xmlHttp.Open "POST", HttpReqURL, False
        xmlHttp.setRequestHeader "Content-Type", "multipart/form-data; boundary=" + multiPartUploadBoundary
        xmlHttp.setRequestHeader "Content-Length", Len(binaryStream.Size)
        xmlHttp.send binaryStream.Read(binaryStream.Size)
    
        binaryStream.Close
    End Sub
    
    Sub AddParameterAndValueToStream(stream As Variant, boundary As String, paramName As String, value As String)
        stream.WriteText "--" + boundary + vbCrLf
        stream.WriteText "Content-Disposition: form-data; name=""" + paramName + """" + vbCrLf
        stream.WriteText vbCrLf
        stream.WriteText value + vbCrLf
    End Sub
    
    Sub AddFileToStream(stream As Variant, boundary As String, name As String, filePath As String)
        Dim fileBytes As String
        fileBytes = ReadBinaryFile(filePath)
    
        stream.WriteText "--" + boundary + vbCrLf
        stream.WriteText "Content-Disposition: form-data; name=""files[" + name + "]""; filename=""" + name + """" + vbCrLf
        stream.WriteText "Content-Type: application/octet-stream" + vbCrLf
        stream.WriteText vbCrLf
        stream.WriteText fileBytes + vbCrLf
    End Sub
    
    Function ReadBinaryFile(strPath)
        Dim oFSO: Set oFSO = CreateObject("Scripting.FileSystemObject")
        Dim oFile: Set oFile = oFSO.GetFile(strPath)
    
        If IsNull(oFile) Then MsgBox ("File not found: " & strPath): Exit Function
    
        With oFile.OpenAsTextStream()
            ReadBinaryFile = .Read(oFile.Size)
            .Close
        End With
    End Function
    
    0 讨论(0)
  • 2021-01-21 14:18

    Please see my "Here it is!" answer. I'm leaving this answer here for historical reasons only.

    Try with something like this:

    ' Set the Server URL to the form input
    HttpReqURL = "https://api.sendgrid.com/api/mail.send.json"
    
    boundary = "----------------------------123456789abc"
    
    Set xmlhttp = CreateObject("MSXML2.XMLHTTP")
    xmlhttp.Open "POST", HttpReqURL, False
    xmlhttp.setRequestHeader "Content-Type", "multipart/form-data; boundary=" + boundary 
    
    dataToSend = "--" + boundary + vbCrLf
    dataToSend = dataToSend + "Content-Disposition: form-data; name=""api_user""" + vbCrLf
    dataToSend = dataToSend + vbCrLf
    dataToSend = dataToSend + YOUR_API_USER + vbCrLf
    
    dataToSend = dataToSend + "--" + boundary + vbCrLf    
    dataToSend = dataToSend + "Content-Disposition: form-data; name=""api_key""" + vbCrLf
    dataToSend = dataToSend + vbCrLf
    dataToSend = dataToSend + YOUR_API_KEY + vbCrLf
    
    dataToSend = dataToSend + "--" + boundary + vbCrLf            
    dataToSend = dataToSend + "Content-Disposition: form-data; name=""to""" + vbCrLf
    dataToSend = dataToSend + vbCrLf
    dataToSend = dataToSend + eTo + vbCrLf
    
    dataToSend = dataToSend + "--" + boundary + vbCrLf
    dataToSend = dataToSend + "Content-Disposition: form-data; name=""toname""" + vbCrLf
    dataToSend = dataToSend + vbCrLf
    dataToSend = dataToSend + vbCrLf
    dataToSend = dataToSend + eToName + vbCrLf
    
    dataToSend = dataToSend + "--" + boundary + vbCrLf
    dataToSend = dataToSend + "Content-Disposition: form-data; name=""subject""" + vbCrLf
    dataToSend = dataToSend + vbCrLf
    dataToSend = dataToSend + eSubject + vbCrLf
    
    dataToSend = dataToSend + "--" + boundary + vbCrLf
    dataToSend = dataToSend + "Content-Disposition: form-data; name=""text""" + vbCrLf
    dataToSend = dataToSend + vbCrLf
    dataToSend = dataToSend + eBody + vbCrLf
    
    dataToSend = dataToSend + "--" + boundary + vbCrLf
    dataToSend = dataToSend + "Content-Disposition: form-data; name=""from""" + vbCrLf
    dataToSend = dataToSend + vbCrLf
    dataToSend = dataToSend + eFrom + vbCrLf
    
    dataToSend = dataToSend + "--" + boundary + vbCrLf
    dataToSend = dataToSend + "Content-Disposition: form-data; name=""files[1]""; filename=""myPDF.pdf""" + vbCrLf
    
    dataToSend = dataToSend + vbCrLf
    dataToSend = dataToSend + "Content-Type: application/octet-stream" + vbCrLf
    dataToSend = dataToSend + vbCrLf
    
    dataToSend = dataToSend + BASE64ENCODEDFILE + vbCrLf
    dataToSend = dataToSend + "--" + boundary + "--" + vbCrLf
    
    xmlhttp.send dataToSend
    
    0 讨论(0)
  • 2021-01-21 14:24

    This code has some additional code and logic to attach multiple attachments:

    Option Explicit
    
    Sub SendEmailUsingSendGrid()
        Dim HttpReqURL As String: HttpReqURL = "https://api.sendgrid.com/api/mail.send.json"
    
        Const adSaveCreateNotExist = 1
        Const adSaveCreateOverWrite = 2
        Const adTypeBinary = 1
        Const adTypeText = 2
        Const adModeReadWrite = 3
    
        Dim YOUR_SG_CREDS_USERNAME As String
        YOUR_SG_CREDS_USERNAME = "username"
    
        Dim YOUR_SG_CREDS_PASSWORD As String
        YOUR_SG_CREDS_PASSWORD = "password"
    
        Dim multiPartUploadBoundary As String
        multiPartUploadBoundary = "123456789abc"
    
        Dim eTo As String
        eTo = "to@example.com"
    
        Dim eToName As String
        eToName = "To Name"
    
        Dim eSubject As String
        eSubject = "My Subject"
    
        Dim eBody As String
        eBody = "This is a test!"
    
        Dim eFrom As String
        eFrom = "from@example.com"
    
        Dim outputStream As Object
        Set outputStream = CreateObject("adodb.stream")
        outputStream.Type = adTypeText
        outputStream.Mode = adModeReadWrite
        outputStream.charset = "windows-1252"
        outputStream.Open
    
        AddParameterAndValueToStream outputStream, multiPartUploadBoundary, "api_user", YOUR_SG_CREDS_USERNAME
        AddParameterAndValueToStream outputStream, multiPartUploadBoundary, "api_key", YOUR_SG_CREDS_PASSWORD
        AddParameterAndValueToStream outputStream, multiPartUploadBoundary, "to", eTo
        AddParameterAndValueToStream outputStream, multiPartUploadBoundary, "toname", eToName
        AddParameterAndValueToStream outputStream, multiPartUploadBoundary, "subject", eSubject
        AddParameterAndValueToStream outputStream, multiPartUploadBoundary, "text", eBody
        AddParameterAndValueToStream outputStream, multiPartUploadBoundary, "from", eFrom
    
        Dim filesToAttach As New Collection
        filesToAttach.Add "C:\temp\test.png"
        filesToAttach.Add "C:\temp\test2.jpg"
    
        AddMultipleFilesToStream outputStream, multiPartUploadBoundary, filesToAttach
    
        outputStream.WriteText "--" + multiPartUploadBoundary + "--" + vbCrLf
    
        Dim binaryStream As Object
        Set binaryStream = CreateObject("ADODB.Stream")
        binaryStream.Mode = 3 'read write
        binaryStream.Type = 1 'adTypeText 'Binary
        binaryStream.Open
    
        ' copy text to binary stream so xmlHttp.send works correctly
        outputStream.Position = 0
        outputStream.CopyTo binaryStream
        outputStream.Close
    
        binaryStream.Position = 0
    
        Dim xmlHttp As Object
        Set xmlHttp = CreateObject("MSXML2.XMLHTTP")
        xmlHttp.Open "POST", HttpReqURL, False
        xmlHttp.setRequestHeader "Content-Type", "multipart/form-data; boundary=" + multiPartUploadBoundary
        xmlHttp.setRequestHeader "Content-Length", Len(binaryStream.Size)
        xmlHttp.send binaryStream.Read(binaryStream.Size)
    
        binaryStream.Close
    End Sub
    
    Sub AddParameterAndValueToStream(stream As Variant, boundary As String, paramName As String, value As String)
        stream.WriteText "--" + boundary + vbCrLf
        stream.WriteText "Content-Disposition: form-data; name=""" + paramName + """" + vbCrLf
        stream.WriteText vbCrLf
        stream.WriteText value + vbCrLf
    End Sub
    
    Sub AddFileToStream(stream As Variant, boundary As String, name As String, filePath As String)
        Dim fileBytes As String
        fileBytes = ReadBinaryFile(filePath)
    
        stream.WriteText "--" + boundary + vbCrLf
        stream.WriteText "Content-Disposition: form-data; name=""files[" + name + "]""; filename=""" + name + """" + vbCrLf
        stream.WriteText "Content-Type: application/octet-stream" + vbCrLf
        stream.WriteText vbCrLf
        stream.WriteText fileBytes + vbCrLf
    End Sub
    
    Sub AddMultipleFilesToStream(stream As Variant, boundary As String, filePaths As Collection)
        Dim fileCount As Integer
        fileCount = filePaths.Count
    
        For n = 1 To fileCount
            Dim fileName As String
            Dim filePath As String
    
            filePath = filePaths(n)
            fileName = Right(filePath, Len(filePath) - InStrRev(filePath, "\"))
    
            AddFileToStream stream, boundary, fileName, filePath
        Next n
    End Sub
    
    Function ReadBinaryFile(strPath)
        Dim oFSO: Set oFSO = CreateObject("Scripting.FileSystemObject")
        Dim oFile: Set oFile = oFSO.GetFile(strPath)
    
        If IsNull(oFile) Then MsgBox ("File not found: " & strPath): Exit Function
    
        With oFile.OpenAsTextStream()
            ReadBinaryFile = .Read(oFile.Size)
            .Close
        End With
    End Function
    
    0 讨论(0)
提交回复
热议问题