Home > Blockchain >  Retrieve file attachment from MMS via ASP.NET MVC controller
Retrieve file attachment from MMS via ASP.NET MVC controller

Time:09-22

I'm trying out the Twilio service to interact with individuals via SMS/MMS. I've sorta figured out how to send MMS messages to initiate the "conversation" and that seems to be working well. However, now I'm trying to build a system to respond to the incoming messages on my SMS/MMS-enabled test number. I'm working from one of the examples I found on the Twilio documentation site to build an ASP.NET MVC web service to handle the conversation (VB.NET):

Imports System.Web.Mvc
Imports Twilio.AspNet.Common
Imports Twilio.AspNet.Mvc
Imports Twilio.TwiML

Namespace Controllers
    Public Class SMSController
        Inherits TwilioController

        ' GET: SMS
        Function Index(ByVal IncomingSMS As SmsRequest) As TwiMLResult
            Dim SMSResponse As New MessagingResponse
            Dim SMSMessage As String = IncomingSMS.Body
            Dim JediCode As String = "There is no emotion, there is peace."
            Dim SithCode As String = "Peace is a lie. There is only Passion."

            JediCode  = vbCrLf & "There is no ignorance, there is knowledge."
            JediCode  = vbCrLf & "There is no passion, there is serenity."
            JediCode  = vbCrLf & "There is no chaos, there is harmony."
            JediCode  = vbCrLf & "There is no death, there is the Force."

            SithCode  = vbCrLf & "Through Passion, I gain Strength."
            SithCode  = vbCrLf & "Through Strength, I gain Power."
            SithCode  = vbCrLf & "Through Power, I gain Victory."
            SithCode  = vbCrLf & "Through Victory my chains are Broken."
            SithCode  = vbCrLf & "The Force shall free me."

            If SMSMessage IsNot Nothing Then
                If SMSMessage.ToUpper.Trim = "JEDI" Then
                    SMSResponse.Message(JediCode)
                ElseIf SMSMessage.ToUpper.Trim = "SITH" Then
                    SMSResponse.Message(SithCode)
                Else
                    SMSResponse.Message("Ahsoka? Is that you?")
                End If
            Else
                SMSResponse.Message("What did you want to know?")
            End If

            Return TwiML(SMSResponse)
        End Function
    End Class
End Namespace

Yes, this is all just "play" stuff that I'm using for testing and will eventually be replaced with something more appropriate to the purpose, but I want to try and figure it all out before I get too deep into the reality of things.

I've set up the site on my IIS server, registered the DNS, and even gotten my SSL certificate set up. Everything seems to be working great with my simple testing so far, but there are a couple of things that I still haven't been able to figure out so far and I'm hoping someone here can point me in the right direction.

I'll ask each as a separate question, but here's the first: how do I retrieve the attachment from an MMS message?

I'd like to be able to receive PDFs (and possibly other file types) and pass them along via email to an appropriate individual or department. I know how to do the emailing, but I haven't been able to find appropriate documentation for how to retrieve the attachment(s) in the MMS message to actually include it in that email process.

When I try to access the properties of the IncomingSMS (SmsRequest) object, I don't find any reference to Media in any of them - no NumMedia, no MediaUri, nothing. There doesn't appear to be an MmsRequest object type (that I've found yet, anyway).

What am I overlooking here to be able to retrieve the PDF I sent to my test number for further processing? Should I change the method's definition to accept the object as a MessageResource or something?

EDIT: I forgot to mention that I checked the Twilio console and see that the message was apparently received successfully with the attachment, so I know that at least that part is working properly.

I've asked a second, related question that goes along with this one to help "finalize" some things for our goals.

CodePudding user response:

The SmsRequest class that you're using comes from the Twilio helper library for ASP.NET which aims to make it easier to integrate Twilio into ASP.NET. However, the SmsRequest and other classes do not cover all possible webhook parameters. If there's parameters missing from the class, you can still retrieve the parameters manually instead of relying on MVC Model Binding.

Based on this C# sample, I created a VB.NET sample to show how to receive and save incoming MMS files:

Imports System.IO
Imports System.Net.Http
Imports System.Threading.Tasks
Imports MimeTypes
Imports Twilio.AspNet.Mvc
Imports Twilio.TwiML
Imports Twilio.TwiML.Messaging

Public Class HomeController
    Inherits TwilioController

    Shared httpClient As HttpClient = New HttpClient

    Async Function Index() As Task(Of TwiMLResult)
        Dim response As New MessagingResponse
        Dim message As New Message

        Dim numMedia = Short.Parse(If(Request.Form.Get("NumMedia"), 0))
        If numMedia = 0 Then
            response.Message("No file received.")
            Return TwiML(response)
        End If

        For mediaIndex As Integer = 0 To numMedia
            Dim mediaUrl = Request.Form.Get($"MediaUrl{mediaIndex}")
            Dim contentType = Request.Form.Get($"MediaContentType{mediaIndex}")
            Dim saveFilePath = Server.MapPath(String.Format(
                "~/App_Data/{0}{1}",
                Path.GetFileName(mediaUrl),
                MimeTypeMap.GetExtension(ContentType)
            ))
            Await DownloadUrlToFileAsync(mediaUrl, saveFilePath)
        Next

        response.Message("File received.")
        Return TwiML(response)
    End Function

    Private Async Function DownloadUrlToFileAsync(mediaUrl As String, saveFilePath As String) As Task
        Dim Response = Await httpClient.GetAsync(mediaUrl)
        Dim httpStream = Await Response.Content.ReadAsStreamAsync()
        Using fileStream As Stream = IO.File.Create(saveFilePath)
            Await httpStream.CopyToAsync(fileStream)
            Await fileStream.FlushAsync()
        End Using
    End Function


End Class

You can probably simplify the code a little by assuming there's only one file going to be sent over MMS, but it's a good idea to handle other cases too.

To get the correct file extension, I'm using this MimeTypeMap library, but you can roll your own solution.

By default the files from the incoming MMS are publicly available via the MediaUrl{mediaIndex} URL, but it's a good idea to turn on Basic Auth for these media files.

If you're turning on Basic Auth for media files, you'll need to add the authentication header to the HTTP requests, like this:

Imports System.IO
Imports System.Net.Http
Imports System.Net.Http.Headers
Imports System.Threading.Tasks
Imports MimeTypes
Imports Twilio.AspNet.Mvc
Imports Twilio.TwiML
Imports Twilio.TwiML.Messaging

Public Class HomeController
    Inherits TwilioController

    Shared httpClient As HttpClient = CreateHttpClient()
    Private Shared Function CreateHttpClient() As HttpClient
        Dim client As New HttpClient
        Dim appSettings As NameValueCollection = ConfigurationManager.AppSettings
        If Boolean.Parse(If(appSettings.Get("TwilioUseBasicAuthForMedia"), False)) Then
            Dim authString = $"{appSettings.Get("TwilioAccountSid")}:{appSettings.Get("TwilioAuthToken")}"
            authString = Convert.ToBase64String(Encoding.ASCII.GetBytes(authString))
            client.DefaultRequestHeaders.Authorization = New AuthenticationHeaderValue("Basic", authString)
        End If

        Return client
    End Function

    ...

End Class

I'm retrieving the TwilioUseBasicAuthForMedia, TwilioAccountSid, and TwilioAuthToken from the Web.config appSettings.

(Make sure you don't check those secrets into source control, and use UserSecretsConfigBuilder instead, to securely set the secrets).

Here's the source code on GitHub.


You're welcome to submit a GitHub issue and/or a PR to add support for these missing parameters to the SmsRequest. Tho, it wouldn't be as easy as normal model binding because the number of parameters increases as more files are sent over MMS.

  • Related