Skip to contentSkip to navigationSkip to topbar
Rate this page:
On this page

Receive and Download Images on Incoming Media Messages with C#


You know how to receive and reply to incoming SMS messages. What if you receive an MMS message containing an image you'd like to download? Let's learn how we can grab that image and any other incoming MMS media using C#.


Create MMS processing project

create-mms-processing-project page anchor

Create an ASP.NET MVC Project

create-an-aspnet-mvc-project page anchor

When Twilio receives a message for your phone number, it can make an HTTP call to a webhook that you create. The easiest way to handle HTTP requests in .NET is to use ASP.NET MVC. You may have an existing ASP.NET MVC project already, or you can create a new, blank project. Just be sure to include the MVC references when going through the project wizard. If you need help creating a new ASP.NET MVC project, check out our mini-guide on the topic.

Twilio expects, at the very least, for your webhook to return a 200 OK response if everything is peachy. Often, however, you will return some TwiML in your response as well. TwiML is just a set of XML commands telling Twilio how you'd like it to respond to your message. Rather than manually generating the XML, we'll use the Twilio.AspNet.Mvc helper library that can make generating the TwiML and the rest of the webhook plumbing easy peasy.

To install the library, open up the Package Manager Console and run the following command:


_10
Install Twilio.AspNet.Mvc Package

Add a new controller called MmsController (again, check out our mini-guide if you are unsure of how to do this). We'll have this class inherit from TwilioController to give us a little easier syntax for returning TwiML. Here's a simple controller that just receives a message and sends a "hello world" reply message.

C# Hello World SMS Webhook

c-hello-world-sms-webhook page anchor

_18
using System.Web.Mvc;
_18
using Twilio.AspNet.Mvc;
_18
using Twilio.TwiML;
_18
using Twilio.TwiML.Mvc;
_18
_18
namespace DownloadMmsImages.Controllers
_18
{
_18
public class MmsController : TwilioController
_18
{
_18
[HttpPost]
_18
public TwiMLResult Index(SmsRequest request)
_18
{
_18
var response = new TwilioResponse();
_18
response.Message("Hello world!");
_18
return TwiML(response);
_18
}
_18
}
_18
}


Receive MMS Message and Images

receive-mms-message-and-images page anchor

Get Incoming Message Details

get-incoming-message-details page anchor

When Twilio calls your webhook, it sends a number of parameters about the message you just received. Most of these, such as the To phone number, the From phone number, and the Body of the message are available as properties of the request parameter to our action method (type SmsRequest). However, one parameter it doesn't have is NumMedia. Thankfully, we can have ASP.NET MVC easily map this parameter for us by adding it to our action method's signature like so:


_10
public TwiMLResult Index(SmsRequest request, int numMedia)

Since an MMS message can have multiple attachments, Twilio will send us form variables named MediaUrlX, where X is a zero-based index. So, for example, the URL for the first media attachment will be in the MediaUrl0 parameter, the second in MediaUrl1, and so on.

In order to handle a dynamic number of attachments, we pull the URLs out of the ASP.NET Request.Form collection like this:


_10
for (var i = 0; i < numMedia; i++)
_10
{
_10
var mediaUrl = Request.Form[$"MediaUrl{i}"];
_10
}

Determine Content Type of Media

determine-content-type-of-media page anchor

Attachments to MMS messages can be of many different file types. JPG(link takes you to an external page) and GIF(link takes you to an external page) images as well as MP4(link takes you to an external page) and 3GP(link takes you to an external page) files are all common. Twilio handles the determination of the file type for you and you can get the standard mime type from the MediaContentTypeX parameter. If you are expecting photos, then you will likely see a lot of attachments with the mime type of image/jpeg.


_10
for (var i = 0; i < numMedia; i++)
_10
{
_10
var mediaUrl = Request.Form[$"MediaUrl{i}"];
_10
var contentType = Request.Form[$"MediaContentType{i}"];
_10
}


Depending on your use case, storing the URLs to the images (or videos or whatever) may be all you need. There's two key features to these URLs that make them very pliable for your use in your apps:

  1. They are publicly accessible without any need for authentication to make sharing easy.
  2. They are permanent (unless you explicitly delete the media, see later).

For example, if you are building a browser-based app that needs to display the images, all you need to do is drop an <img src="twilio url to your image"> tag into the page. If this works for you, then perhaps all you need is to store the URL in a database character field.

Save media to local file system

save-media-to-local-file-system page anchor

If you want to save the media attachments to a file, then you will need to make an HTTP request to the media URL and write the response stream to a file. If you need a unique filename, you can use the last part of the media URL. For example, suppose your media URL is the following:


_10
https://api.twilio.com/2010-04-01/Accounts/ACxxxx/Messages/MMxxxx/Media/ME27be8a708784242c0daee207ff73db67

You can use that last part of the URL as a unique filename. Figuring out a good extension to use is a little trickier. If you are only expecting images, you could just assume a ".jpg" extension. For a little more flexibility, you can look up the mime type and determine a good extension to use based on that.

Here's the complete code for our controller that saves each MMS attachment to the App_Data folder:

Process MMS Media with C#

process-mms-media-with-c page anchor

_75
using System.Diagnostics;
_75
using System.IO;
_75
using System.Net.Http;
_75
using System.Threading.Tasks;
_75
using System.Web.Mvc;
_75
using Microsoft.Win32;
_75
using Twilio.AspNet.Common;
_75
using Twilio.AspNet.Mvc;
_75
using Twilio.TwiML;
_75
using Task = System.Threading.Tasks.Task;
_75
_75
namespace DownloadMmsImages.Controllers
_75
{
_75
public class MmsController : TwilioController
_75
{
_75
private const string SavePath = "~/App_Data/";
_75
_75
[HttpPost]
_75
public async Task<TwiMLResult> Index(SmsRequest request, int numMedia)
_75
{
_75
for (var i = 0; i < numMedia; i++)
_75
{
_75
var mediaUrl = Request.Form[$"MediaUrl{i}"];
_75
Trace.WriteLine(mediaUrl);
_75
var contentType = Request.Form[$"MediaContentType{i}"];
_75
_75
var filePath = GetMediaFileName(mediaUrl, contentType);
_75
await DownloadUrlToFileAsync(mediaUrl, filePath);
_75
}
_75
_75
var response = new MessagingResponse();
_75
var body = numMedia == 0 ? "Send us an image!" :
_75
$"Thanks for sending us {numMedia} file(s)!";
_75
response.Message(body);
_75
return TwiML(response);
_75
}
_75
_75
private string GetMediaFileName(string mediaUrl,
_75
string contentType)
_75
{
_75
return Server.MapPath(
_75
// e.g. ~/App_Data/MExxxx.jpg
_75
SavePath +
_75
Path.GetFileName(mediaUrl) +
_75
GetDefaultExtension(contentType)
_75
);
_75
}
_75
_75
private static async Task DownloadUrlToFileAsync(string mediaUrl,
_75
string filePath)
_75
{
_75
using (var client = new HttpClient())
_75
{
_75
var response = await client.GetAsync(mediaUrl);
_75
var httpStream = await response.Content.ReadAsStreamAsync();
_75
using (var fileStream = System.IO.File.Create(filePath))
_75
{
_75
await httpStream.CopyToAsync(fileStream);
_75
await fileStream.FlushAsync();
_75
}
_75
}
_75
}
_75
_75
public static string GetDefaultExtension(string mimeType)
_75
{
_75
// NOTE: This implementation is Windows specific (uses Registry)
_75
// Platform independent way might be to download a known list of
_75
// mime type mappings like: http://bit.ly/2gJYKO0
_75
var key = Registry.ClassesRoot.OpenSubKey(
_75
@"MIME\Database\Content Type\" + mimeType, false);
_75
var ext = key?.GetValue("Extension", null)?.ToString();
_75
return ext ?? "application/octet-stream";
_75
}
_75
}
_75
}

Notice we have made our controller action async(link takes you to an external page). This is highly recommended since we will be making a network request that could take a little time. Doing this asynchronously means that we won't block other requests from being handled while the file downloads.

Another idea for these image files could be uploading them to a cloud storage service like Azure Blob Storage(link takes you to an external page) or Amazon S3(link takes you to an external page). You could also save them to a database, if necessary. They're just regular files at this point. Go crazy.

Delete media from Twilio

delete-media-from-twilio page anchor

If you are downloading the attachments and no longer need them to be stored by Twilio, you can easily delete them. You can send an HTTP DELETE request to the media URL and it will be deleted, but you will need to be authenticated to do this. To make this easy, you can use the Twilio C# Helper Library(link takes you to an external page).

C#

_24
// Install the C# / .NET helper library from twilio.com/docs/csharp/install
_24
_24
using System;
_24
using Twilio;
_24
using Twilio.Rest.Api.V2010.Account.Message;
_24
_24
_24
class Program
_24
{
_24
static void Main(string[] args)
_24
{
_24
// Find your Account SID and Auth Token at twilio.com/console
_24
// and set the environment variables. See http://twil.io/secure
_24
string accountSid = Environment.GetEnvironmentVariable("TWILIO_ACCOUNT_SID");
_24
string authToken = Environment.GetEnvironmentVariable("TWILIO_AUTH_TOKEN");
_24
_24
TwilioClient.Init(accountSid, authToken);
_24
_24
MediaResource.Delete(
_24
pathMessageSid: "MM800f449d0399ed014aae2bcc0cc2f2ec",
_24
pathSid: "ME557ce644e5ab84fa21cc21112e22c485"
_24
);
_24
}
_24
}

(warning)

Warning

Twilio supports HTTP Basic and Digest Authentication. Authentication allows you to password protect your TwiML URLs on your web server so that only you and Twilio can access them. Learn more about HTTP authentication and validating incoming requests here.


All the code, in a complete working project, is available on GitHub(link takes you to an external page). If you need to dig a bit deeper, you can head over to our API Reference and learn more about the Twilio webhook request and the REST API Media resource. Also, you will want to be aware of the pricing(link takes you to an external page) for storage of all the media files that you keep on Twilio's servers.

We'd love to hear what you build with this.


Rate this page: