Multipart Form Data In WCF

In this article, you will learn about multipart form data in WCF.

Step 1: Create a New WCF Service Project (WCF Service Library).

  1. On the File menu, click New Project.
  2. In the New Project dialog box under Project types, expand Visual C#, click WCF and select WCF Service Library. In the Name box, type "DemoMultipartWCF". In the Solution name, type "WCFDemo" and then click OK.
  3. Now, delete the default created Interface, Class perspective "IService1.cs" and "Service1.cs".
  4. Add a New Interface by right clicking on DemoMultipartWCF(project) > Add > New Item and select "WCF Service". Name it as "Users.svc", click ADD.

Step 2: Create an Interface and implement its body.

  1. Create a New Interface in IUsers.cs File (Paste the code, given below):
    1. [OperationContract]  
    2. [WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedResponse, UriTemplate = "UpdateUserDetail")]  
    3. bool UpdateUserDetail(Stream stream);  
  2. Implement the body of UpdateUserDetail Interface in Users.svc file (Paste the code, given below):
    1. public bool UpdateUserDetail(Stream stream)  
    2. {  
    3. return false;  
    4. }  

Step 3: Create a MultipartParser to read the requested form data.

Create a New folder name as "DataContract" in your project, add "CommonDC" class in the folder. Paste the code, given below, in "CommonDC.cs File" class.

Uses Of All Below Method

  • Parse (Stream stream, Encoding encoding) - This method is used to check the input stream in UTF8 (Unicode Transformation Format).

  • ParseParameter (Stream stream, Encoding encoding) - This method is used to parse the parameter from the stream
    IndexOf(byte[] searchWithin, byte[] serachFor, int startIndex) - This method is separate from all the requested parameters, which are in the stream.

  • ToByteArray(Stream stream) - This method is used to convert the stream to byteArray
    1. public class MultipartParser {  
    2.     private byte[] requestData;  
    3.     //Require Namespace: using System.IO;  
    4.     public MultipartParser(Stream stream) {  
    5.         //Require Namespace: using System.Text;  
    6.         this.Parse(stream, Encoding.UTF8);  
    7.         ParseParameter(stream, Encoding.UTF8);  
    8.     }  
    9.     public MultipartParser(Stream stream, Encoding encoding) {  
    10.         this.Parse(stream, encoding);  
    11.     }  
    12.     private void Parse(Stream stream, Encoding encoding) {  
    13.         this.Success = false;  
    14.         // Read the stream into a byte array  
    15.         byte[] data = ToByteArray(stream);  
    16.         requestData = data;  
    17.         // Copy to a string for header parsing  
    18.         string content = encoding.GetString(data);  
    19.         // The first line should contain the delimiter  
    20.         int delimiterEndIndex = content.IndexOf("\r\n");  
    21.         if (delimiterEndIndex > -1) {  
    22.             string delimiter = content.Substring(0, content.IndexOf("\r\n"));  
    23.             //Require Namespace: using System.Text.RegularExpressions;  
    24.             // Look for Content-Type  
    25.             Regex re = new Regex(@ "(?<=Content\-Type:)(.*?)(?=\r\n\r\n)");  
    26.             Match contentTypeMatch = re.Match(content);  
    27.             // Look for filename  
    28.             re = new Regex(@ "(?<=filename\=\"")(.* ? )( ? = \"")  
    29.             ");  
    30.             Match filenameMatch = re.Match(content);  
    31.             // Did we find the required values?  
    32.             if (contentTypeMatch.Success && filenameMatch.Success) {  
    33.                 // Set properties  
    34.                 this.ContentType = contentTypeMatch.Value.Trim();  
    35.                 this.Filename = filenameMatch.Value.Trim();  
    36.                 // Get the start & end indexes of the file contents  
    37.                 int startIndex = contentTypeMatch.Index + contentTypeMatch.Length + "\r\n\r\n".Length;  
    38.                 byte[] delimiterBytes = encoding.GetBytes("\r\n" + delimiter);  
    39.                 int endIndex = IndexOf(data, delimiterBytes, startIndex);  
    40.                 int contentLength = endIndex - startIndex;  
    41.                 // Extract the file contents from the byte array  
    42.                 byte[] fileData = new byte[contentLength];  
    43.                 Buffer.BlockCopy(data, startIndex, fileData, 0, contentLength);  
    44.                 this.FileContents = fileData;  
    45.                 this.Success = true;  
    46.             }  
    47.         }  
    48.     }  
    49.     private void ParseParameter(Stream stream, Encoding encoding) {  
    50.         this.Success = false;  
    51.         // Read the stream into a byte array  
    52.         byte[] data;  
    53.         if (requestData.Length == 0) {  
    54.             data = ToByteArray(stream);  
    55.         } else {  
    56.             data = requestData;  
    57.         }  
    58.         // Copy to a string for header parsing  
    59.         string content = encoding.GetString(data);  
    60.         // The first line should contain the delimiter  
    61.         int delimiterEndIndex = content.IndexOf("\r\n");  
    62.         if (delimiterEndIndex > -1) {  
    63.             string delimiter = content.Substring(0, content.IndexOf("\r\n"));  
    64.             string[] splitContents = content.Split(new [] {  
    65.                 delimiter  
    66.             }, StringSplitOptions.RemoveEmptyEntries);  
    67.             foreach(string t in splitContents) {  
    68.                 // Look for Content-Type  
    69.                 Regex contentTypeRegex = new Regex(@ "(?<=Content\-Type:)(.*?)(?=\r\n\r\n)");  
    70.                 Match contentTypeMatch = contentTypeRegex.Match(t);  
    71.                 // Look for name of parameter  
    72.                 Regex re = new Regex(@ "(?<=name\=\"")(.*)  
    73.                 ");  
    74.                 Match name = re.Match(t);  
    75.                 // Look for filename  
    76.                 re = new Regex(@ "(?<=filename\=\"")(.* ? )( ? = \"")  
    77.                 ");  
    78.                 Match filenameMatch = re.Match(t);  
    79.                 // Did we find the required values?  
    80.                 if (name.Success || filenameMatch.Success) {  
    81.                     // Set properties  
    82.                     //this.ContentType = name.Value.Trim();  
    83.                     int startIndex;  
    84.                     if (filenameMatch.Success) {  
    85.                         this.Filename = filenameMatch.Value.Trim();  
    86.                     }  
    87.                     if (contentTypeMatch.Success) {  
    88.                         // Get the start & end indexes of the file contents  
    89.                         startIndex = contentTypeMatch.Index + contentTypeMatch.Length + "\r\n\r\n".Length;  
    90.                     } else {  
    91.                         startIndex = name.Index + name.Length + "\r\n\r\n".Length;  
    92.                     }  
    93.                     //byte[] delimiterBytes = encoding.GetBytes("\r\n" + delimiter);  
    94.                     //int endIndex = IndexOf(data, delimiterBytes, startIndex);  
    95.                     //int contentLength = t.Length - startIndex;  
    96.                     string propertyData = t.Substring(startIndex - 1, t.Length - startIndex);  
    97.                     // Extract the file contents from the byte array  
    98.                     //byte[] paramData = new byte[contentLength];  
    99.                     //Buffer.BlockCopy(data, startIndex, paramData, 0, contentLength);  
    100.                     MyContent myContent = new MyContent();  
    101.                     myContent.Data = encoding.GetBytes(propertyData);  
    102.                     myContent.StringData = propertyData;  
    103.                     myContent.PropertyName = name.Value.Trim().TrimEnd('"');  
    104.                     if (MyContents == null) MyContents = new List < MyContent > ();  
    105.                     MyContents.Add(myContent);  
    106.                     this.Success = true;  
    107.                 }  
    108.             }  
    109.         }  
    110.     }  
    111.     private int IndexOf(byte[] searchWithin, byte[] serachFor, int startIndex) {  
    112.         int index = 0;  
    113.         int startPos = Array.IndexOf(searchWithin, serachFor[0], startIndex);  
    114.         if (startPos != -1) {  
    115.             while ((startPos + index) < searchWithin.Length) {  
    116.                 if (searchWithin[startPos + index] == serachFor[index]) {  
    117.                     index++;  
    118.                     if (index == serachFor.Length) {  
    119.                         return startPos;  
    120.                     }  
    121.                 } else {  
    122.                     startPos = Array.IndexOf < byte > (searchWithin, serachFor[0], startPos + index);  
    123.                     if (startPos == -1) {  
    124.                         return -1;  
    125.                     }  
    126.                     index = 0;  
    127.                 }  
    128.             }  
    129.         }  
    130.         return -1;  
    131.     }  
    132.     private byte[] ToByteArray(Stream stream) {  
    133.         byte[] buffer = new byte[32768];  
    134.         using(MemoryStream ms = new MemoryStream()) {  
    135.             while (true) {  
    136.                 int read = stream.Read(buffer, 0, buffer.Length);  
    137.                 if (read <= 0) return ms.ToArray();  
    138.                 ms.Write(buffer, 0, read);  
    139.             }  
    140.         }  
    141.     }  
    142.     public List < MyContent > MyContents {  
    143.         get;  
    144.         set;  
    145.     }  
    146.     public bool Success {  
    147.         get;  
    148.         private set;  
    149.     }  
    150.     public string ContentType {  
    151.         get;  
    152.         private set;  
    153.     }  
    154.     public string Filename {  
    155.         get;  
    156.         private set;  
    157.     }  
    158.     public byte[] FileContents {  
    159.         get;  
    160.         private set;  
    161.     }  
    162. }  
    163. public class MyContent {  
    164.     public byte[] Data {  
    165.         get;  
    166.         set;  
    167.     }  
    168.     public string PropertyName {  
    169.         get;  
    170.         set;  
    171.     }  
    172.     public string StringData {  
    173.         get;  
    174.         set;  
    175.     }  
    176. }  

Step 4: Implement Pre-Required Method like SaveImageFile & GetEncoder.

  1. Create a Method to save the image file.
    1. public static bool SaveImageFile(byte[] ImageFileContent, string ImagePathWithImageName)  
    2. {  
    3.     try {  
    4.         //Require Namespace: using System.Drawing  
    5.         Image image;  
    6.         //Read Image File  
    7.         using(MemoryStream ms = new MemoryStream(ImageFileContent)) {  
    8.             image = Image.FromStream(ms);  
    9.         }  
    10.         Bitmap bmp = new Bitmap(image);  
    11.         //Require Namespace: System.Drawing.Imaging  
    12.         ImageCodecInfo jgpEncoder = GetEncoder(ImageFormat.Jpeg);  
    13.         //We need to write Encoder with Root otherwise it is an ambiguous between "System.Drawing.Imaging.Encoder" and "System.Text.Encoder"  
    14.         System.Drawing.Imaging.Encoder myEncoder = System.Drawing.Imaging.Encoder.Quality;  
    15.         EncoderParameters myEncoderParameters = new EncoderParameters(1);  
    16.         EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, 40 L);  
    17.         myEncoderParameters.Param[0] = myEncoderParameter;  
    18.         bmp.Save(ImagePathWithImageName, jgpEncoder, myEncoderParameters);  
    19.         return true;  
    20.     } catch {  
    21.         //Do Code For Log or Handle Exception   
    22.         return false;  
    23.     }  
    24. }  
    Note: We need to write Encoder with Root, else it is an ambiguity between "System.Drawing.Imaging.Encoder" and "System.Text.Encoder".
    Encoder

  2. Create a method to retrieve the image codecs.
    1. public static ImageCodecInfo GetEncoder(ImageFormat format)  
    2. {  
    3.     ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();  
    4.     foreach(ImageCodecInfo codec in codecs) {  
    5.         if (codec.FormatID == format.Guid) {  
    6.             return codec;  
    7.         }  
    8.     }  
    9.     return null;  
    10. }  
    What is the use of ImageCodecInfo ?

    It provides the necessary storage members and methods to retrieve all the pertinent information about the installed image codecs.

Step 5: Now, it's time to implement our interface - UpdateUserDetail. (Paste the code, given below):

  1. public bool UpdateUserDetail(Stream stream) {  
  2.     try {  
  3.         //Create an Object of byte[]  
  4.         byte[] buf = new byte[10000000];  
  5.         //Initialise an Object of MultipartParser Class With Requested Stream  
  6.         MultipartParser parser = new MultipartParser(stream);  
  7.         //Check that we have not null value in requested stream  
  8.         if (parser != null && parser.Success) {  
  9.             //Fetch Requested Formdata (content)   
  10.             //(for this example our requested formdata are UserName[String])  
  11.             foreach(var item in parser.MyContents) {  
  12.                     //Check our requested fordata  
  13.                     if (item.PropertyName == "UserName") {  
  14.                         string RequestedName = item.StringData;  
  15.                     }  
  16.                 }  
  17.                 //Create a GUID for Image Name  
  18.             string ImageName = Guid.NewGuid().ToString();  
  19.             //Image Path  
  20.             string SaveImagePath = "D:/DemoProject/DemoMultipartWCF/DemoMultipartWCF/MyFile/";  
  21.             //Ensure That WE have the right path and Directory  
  22.             if (!Directory.Exists(SaveImagePath)) {  
  23.                 //If Directory Not Exists Then Create a Directory  
  24.                 Directory.CreateDirectory(SaveImagePath);  
  25.             }  
  26.             //Fetch File Content & Save that Image HERE (for this example our requested FileContent is ProfilePicture[File])  
  27.             string ImagePathWithImageName = SaveImagePath + ImageName + ".bmp";  
  28.             SaveImageFile(parser.FileContents, ImagePathWithImageName);  
  29.             return true;  
  30.         }  
  31.         return false;  
  32.     } catch {  
  33.         //Do Code For Log or Handle Exception   
  34.         return false;  
  35.     }  
  36. }  
Step 6: Now, all is done. Ensure you have the following setting in your Web.config:
  1. <system.serviceModel>  
  2.     <bindings>  
  3.         <basicHttpBinding>  
  4.             <binding maxReceivedMessageSize="999999999" maxBufferSize="999999999" maxBufferPoolSize="9999999999" name="ITransactionProcessor">  
  5.                 <readerQuotas maxDepth="32" maxArrayLength="999999999" maxStringContentLength="999999999" />  
  6.                 <security mode="TransportWithMessageCredential" /> </binding>  
  7.         </basicHttpBinding>  
  8.     </bindings>  
  9.     <behaviors>  
  10.         <serviceBehaviors>  
  11.             <behavior>  
  12.                 <!-- To avoid disclosing metadata information, set the values below to false before deployment -->  
  13.                 <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />  
  14.                 <!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->  
  15.                 <serviceDebug includeExceptionDetailInFaults="true" /> </behavior>  
  16.         </serviceBehaviors>  
  17.         <endpointBehaviors>  
  18.             <behavior name="web">  
  19.                 <webHttp helpEnabled="true" /> </behavior>  
  20.         </endpointBehaviors>  
  21.     </behaviors>  
  22.     <protocolMapping>  
  23.         <add binding="basicHttpsBinding" scheme="https" /> </protocolMapping>  
  24.     <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />  
  25.     <services>  
  26.         <service name="DemoMultipartWCF.Users">  
  27.             <endpoint name="UsersXML" address="" binding="basicHttpBinding" contract="DemoMultipartWCF.IUsers"></endpoint>  
  28.             <endpoint name="UsersJSON" address="json" binding="webHttpBinding" contract="DemoMultipartWCF.IUsers" behaviorConfiguration="web"></endpoint>  
  29.         </service>  
  30.     </services>  
  31.     <client>  
  32.         <endpoint address="https://ics2wstest.ic3.com/commerce/1.x/transactionProcessor" binding="basicHttpBinding" bindingConfiguration="ITransactionProcessor" contract="com.cybersource.api.ITransactionProcessor" name="portXML" /> </client>  
  33. </system.serviceModel>  
Now, it's complementary time.

How To Test This WCF Service ? 
  1. You Need to install POSTMAN or ADVANCE REST SHARP in Chrome Browser. (There are also some other tools, available to test WCF Services).

  2. Open Postman and enter the requested URL.

    WCF Service

  3. Select POST Method.

    POST

  4. Select Body and Fill Form Data. (Requested Data: Username & Requested File: ProfilePicture [browse and select the image]).

    Body

  5. Now, just hit Send and here we go.