Requirements

As an Architect, I need to design an application that would receive a file within a Form-Data posted from a Web Application. The file is to be inserted as an attachment to a Azure Cosmos DB Document. The file needs to be accessible from all the Azure Locations.

NOTE: There are two ways to create an Attachment for a Document.

  1. Upload the fie to an external blob service and store the metadata for the Attachment in Azure Cosmos DB.
  2. Store the Attachment media/blog with the Document so it’s managed by Cosmos DB.

Architecture

Architecture

Use Case

  • The Web Application is part of an IoT solution.
  • The user would select a document name from a drop-down list. The value would be the Document id for the Attachment.
  • The user would select a zip file that contains the software update for a device.
  • The user would Send the form-data to a Function App.
  • The function app would return the StatusCode and the Resource data of the attachment.

Development

INFO: Rather than “reinvent the wheel” I referenced the Http Multipart Parser in the Function App.

The following is the source code for my Function App

using System.Configuration;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using HttpMultipartParser;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;

#endregion

namespace CosmosDBFormData
{
    public static class FormData
    {
        private static readonly string Endpoint = ConfigurationManager.AppSettings["endpoint"];
        private static readonly string AuthKey = ConfigurationManager.AppSettings["authKey"];
        private static readonly string Database = ConfigurationManager.AppSettings["database"];
        private static readonly string Collection = ConfigurationManager.AppSettings["collection"];

        private static readonly DocumentClient Client = new DocumentClient(new Uri(Endpoint), AuthKey,
            new ConnectionPolicy
            {
            
                ConnectionMode = ConnectionMode.Direct,
                ConnectionProtocol = Protocol.Tcp
            }
        );

        [FunctionName("FormData")]
        public static async Task<HttpResponseMessage> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]
            HttpRequestMessage req, TraceWriter log)
        {
            var data = req.Content.ReadAsStreamAsync();


            var parser = new MultipartFormDataParser(data.Result);
            var id = parser.GetParameterValue("id");
            var file = parser.Files.FirstOrDefault();
            if (file == null) return req.CreateResponse(HttpStatusCode.BadRequest);
            var fileName = file.FileName;
            var attachment = file.Data;

            var mediaOptions = new MediaOptions
            {
                Slug = fileName,
                ContentType = file.ContentType
            };


            try
            {
                var resp = await Client.CreateAttachmentAsync(UriFactory.CreateDocumentUri(Database, Collection, id),
                    attachment, mediaOptions);
            

                return req.CreateResponse(HttpStatusCode.OK, result.Resource);
            }
            catch (DocumentClientException ex)
            {
                var statusCode = ex.StatusCode;
                return req.CreateErrorResponse((HttpStatusCode) statusCode, ex.Message);
            }
        }
    }
}

To test my function, I used Postman, as shown in the following figure.

The following figure shows the Sucess Result.

Success

The following is a sample of an Error response

Failed

Summary

  • A Function App can be used to recive Form-Data
  • Using a Http Multipart/form-data parser eliminates custom code.
  • Inserting a Cosmos DB Attachent as an embeded resource provides access to the attachhment from any Azure Location.

Next Steps

  • Modifying the Function App to support creation of the Document along with the attachment.