How to post multipart data in C# via WebRequest

This is a small code sample showing how you can post an image for example to a http server REST API endpoint.

 /// 
    /// Class to show how to do a MultiPartRequest
    /// For more information about MultiPart requests, look for this link : https://code.msdn.microsoft.com/windowsapps/WP8-Post-Multipart-Data-62fbbf72 
    /// 
    class MultipartRequest
    {
        #region Constants

        /// 
        /// The encoding in UTF8
        /// 
        private static readonly Encoding Encoding = Encoding.UTF8;

        /// 
        /// Guid of the cardholder to set the picture to.
        /// 
        private readonly Guid m_cardholderGuid;

        /// 
        /// The image to send.
        /// 
        private readonly Image m_image;

        /// 
        /// The Image Name, extracted from the Path to the image.
        /// 
        private readonly string m_imageName;

        /// 
        /// The server uri. Does not contain any query.
        /// 
        private readonly string m_uriServer;

        /// 
        /// The webSdkStudioWindow
        /// 
        private readonly WebSdkStudioWindow m_webSdkStudioWindow;

        #endregion

        #region Nested Classes and Structures

        public class FileParameter
        {
            #region Properties

            public string ContentType { get; set; }

            public byte[] File { get; set; }

            public string FileName { get; set; }

            #endregion

            #region Constructors

            public FileParameter(byte[] file) : this(file, null) { }

            public FileParameter(byte[] file, string filename) : this(file, filename, null) { }

            public FileParameter(byte[] file, string filename, string contenttype)
            {
                File = file;
                FileName = filename;
                ContentType = contenttype;
            }

            #endregion
        }

        #endregion

        #region Constructors

        public MultipartRequest(string image, string uriServer, Guid cardholderGuid, WebSdkStudioWindow webSdkStudioWindow)
        {
            m_image = Image.FromFile(image);
            m_uriServer = uriServer;
            string[] imageSplit = image.Split('\\');
            m_imageName = imageSplit.Last();
            m_cardholderGuid = cardholderGuid;
            m_webSdkStudioWindow = webSdkStudioWindow;
        }

        #endregion

        #region Public Methods

        public void UploadData()
        {
            // Generate post objects
            Dictionary postParameters = new Dictionary();

            //String parameters
            string myPicture = ImageToBase64String(m_image);
            byte[] myPictureArray = Encoding.GetBytes(myPicture);

            postParameters.Add("$myPicture", new FileParameter(myPictureArray, m_imageName, "image/png"));

            MultipartFormDataPost(
                m_uriServer + "entity?q=entity=" + m_cardholderGuid + ",Picture=$myPicture",
                postParameters);


        }

        #endregion

        #region Private Methods

        private static byte[] GetMultipartFormData(Dictionary postParameters, string boundary)
        {

            Stream formDataStream = new MemoryStream();
            bool needsClrf = false;
            try
            {
                foreach (var param in postParameters)
                {
                    // Thanks to feedback from commenters, add a CRLF to allow multiple parameters to be added.
                    // Skip it on the first parameter, add it to subsequent parameters.
                    if (needsClrf)
                        formDataStream.Write(Encoding.GetBytes(Environment.NewLine), 0, Encoding.GetByteCount(Environment.NewLine));

                    needsClrf = true;

                    FileParameter value = param.Value as FileParameter;
                    if (value != null)
                    {
                        FileParameter fileToUpload = value;

                        // Add just the first part of this param, since we will write the file data directly to the Stream
                        string header = string.Format(
                            "--{0}Content-Disposition: form-data; name=\"{1}\"; filename=\"{2}\"{3}Content-Type: {4}",
                            new StringBuilder(boundary).Append(Environment.NewLine),
                            param.Key,
                            fileToUpload.FileName ?? param.Key,
                            Environment.NewLine,
                            new StringBuilder(fileToUpload.ContentType ?? "application/octet-stream").Append(Environment.NewLine).Append(Environment.NewLine));

                        formDataStream.Write(Encoding.GetBytes(header), 0, Encoding.GetByteCount(header));

                        // Write the file data directly to the Stream, rather than serializing it to a string.
                        formDataStream.Write(fileToUpload.File, 0, fileToUpload.File.Length);
                    }
                    else
                    {
                        string postData = string.Format("--{0}Content-Disposition: form-data; name=\"{1}\"{2}",
                            new StringBuilder(boundary).Append(Environment.NewLine),
                            param.Key,
                            new StringBuilder(Environment.NewLine).Append(Environment.NewLine).Append(param.Value));
                        formDataStream.Write(Encoding.GetBytes(postData), 0, Encoding.GetByteCount(postData));
                    }
                }

                // Add the end of the request.  Start with a newline
                string footer = new StringBuilder(Environment.NewLine).Append("--").Append(boundary).Append("--").Append(Environment.NewLine).ToString();
                formDataStream.Write(Encoding.GetBytes(footer), 0, Encoding.GetByteCount(footer));
            }
            catch (Exception ex)
            {
                throw new Exception("Network Issue : ", ex);
            }
            // Dump the Stream into a byte[]
            formDataStream.Position = 0;
            byte[] formData = new byte[formDataStream.Length];
            formDataStream.Read(formData, 0, formData.Length);
            formDataStream.Close();
            return formData;
        }

        private static string ImageToBase64String(Image image)
        {
            byte[] imageBytes = ImageToByteArray(image);
            return Convert.ToBase64String(imageBytes);
        }

        private static byte[] ImageToByteArray(Image image)
        {
            var ms = new MemoryStream();
            image.Save(ms, ImageFormat.Png);
            return ms.ToArray();
        }

        private void MultipartFormDataPost(string url, Dictionary postParameters)
        {
            string formDataBoundary = string.Format("----------{0:N}", Guid.NewGuid());
            string contentType = "multipart/form-data; boundary=" + formDataBoundary;

            byte[] formData = GetMultipartFormData(postParameters, formDataBoundary);

            PostForm(url, contentType, formData);
        }

        private void PostForm(string url, string contentType, byte[] formData)
        {
            Request request = new Request("POST", url);
            HttpWebRequest httpWebRequest = WebRequest.Create(request.Url) as HttpWebRequest;

            if (httpWebRequest == null)
            {
                throw new NullReferenceException("request is not a http request");
            }

            // Set up the request properties.
            httpWebRequest.Method = request.HttpMethod;
            httpWebRequest.ContentType = contentType;
            httpWebRequest.CookieContainer = new CookieContainer();
            httpWebRequest.ContentLength = formData.Length;
            httpWebRequest.Credentials = request.WebRequestCredentials;

            httpWebRequest.BeginGetRequestStream(result =>
            {
                try
                {
                    HttpWebRequest webRequest = (HttpWebRequest)result.AsyncState;
                    using (Stream requestStream = webRequest.EndGetRequestStream(result))
                    {
                        requestStream.Write(formData, 0, formData.Length);
                        requestStream.Close();
                    }
                    webRequest.BeginGetResponse(ar =>
                    {
                        try
                        {
                            WebResponse response = webRequest.EndGetResponse(ar);
                            Stream responseStream = response.GetResponseStream();
                            if (responseStream != null)
                            {
                                using (StreamReader sr = new StreamReader(responseStream))
                                {
                                    request.Response = sr.ReadToEnd();
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            request.Response = ex.Message;
                        }
                        m_webSdkStudioWindow.OnMultipartResponse(request);
                    }, null);
                }
                catch (Exception ex)
                {
                    request.Response = ex.Message;
                    m_webSdkStudioWindow.OnMultipartResponse(request);
                }
            }, httpWebRequest);

        }

        #endregion
    }

    #endregion
}