Search code examples
c#regexwcfmultipartform-data

WCF service to consume Multipart data from android


I am using android okhttp to send multipart data i.e image and text to wcf service. I am able to send and parse partial data at the server using a custom MultipartParser class as mentioned here.

When my file gets written to the server it gets corrupted due to some unwanted chars from the Multipart data gets written with bytes[I found this out by opening the image in notepad++ which showed encoded bytes and unwanted chars]. I am able to remove those by adding the regex matcher for contentLenMatch but still a newline is being written at the start of the file, hence showing it as corrupted.

I need to know how to remove the blank spaces or \n from the data before writing it to a file.

Code :

     public class MultipartParser    {
     public IDictionary<string, string> Parameters = new Dictionary<string, string>();
     public MultipartParser(Stream stream)        {
        this.Parse(stream, Encoding.UTF8);
    }
    public MultipartParser(Stream stream, Encoding encoding)        {
        this.Parse(stream, encoding);
    }
    public string getcontent(Stream stream, Encoding encoding)        {
        byte[] data = ToByteArray(stream);
        string content = encoding.GetString(data);
        string delimiter = content.Substring(0, content.IndexOf("\r\n"));
        string[] sections = content.Split(new string[] { delimiter }, StringSplitOptions.RemoveEmptyEntries);
        foreach (string s in sections)       {
            Match nameMatch = new Regex(@"(?<=name\=\"")(.*?)(?=\"")").Match(s);
            string name = nameMatch.Value.Trim().ToLower();
            if (!string.IsNullOrWhiteSpace(name))        {
                int startIndex = nameMatch.Index + nameMatch.Length + "\r\n\r\n".Length;    
            }            
        string strRet = ""; //Parameters["name"];
        return strRet;
    }
    private void Parse(Stream stream, Encoding encoding)        {
        this.Success = false;
        byte[] data = ToByteArray(stream);
        string content = encoding.GetString(data);
        int delimiterEndIndex = content.IndexOf("\r\n");
        if (delimiterEndIndex > -1)    {
            string delimiter = content.Substring(0, content.IndexOf("\r\n"));
            Regex re = new Regex(@"(?<=Content\-Type:)(.*?)");
            Match contentTypeMatch = re.Match(content);
            re = new Regex(@"(?<=filename\=\"")(.*?)(?=\"")");
            Match filenameMatch = re.Match(content);


            // to match Content-Length and remove ***Unwanted chars***
            re = new Regex(@"(?<=Content\-Length:)(.*)");
            Match contentLenMatch = re.Match(content);
            if(contentLenMatch.Success){
                contentLenMatch = contentLenMatch.NextMatch();
            }


            if (contentTypeMatch.Success && filenameMatch.Success)    {
                this.ContentType = contentTypeMatch.Value.Trim();
                this.Filename = filenameMatch.Value.Trim();


                // changed from contentTypeMatch to contentLenMatch
                // startIndex must point to the 1st byte 
                // from where the file needs to be written
                // Need to remove extra \n that gets written to the file
                int startIndex = contentLenMatch.Index + contentLenMatch .Length + "\r\n\r\n".Length;


                byte[] delimiterBytes = encoding.GetBytes("\r\n" + delimiter);
                int endIndex = IndexOf(data, delimiterBytes, startIndex);
                int contentLength = endIndex - startIndex;
                byte[] fileData = new byte[contentLength];
                Buffer.BlockCopy(data, startIndex, fileData, 0, contentLength);
                this.FileContents = fileData;
                this.Success = true;                }
        }        }
    private int IndexOf(byte[] searchWithin, byte[] serachFor, int startIndex)       {
        int index = 0;
        int startPos = Array.IndexOf(searchWithin, serachFor[0], startIndex);
        if (startPos != -1)          {
            while ((startPos + index) < searchWithin.Length)             {
                if (searchWithin[startPos + index] == serachFor[index])                   {
                    index++;
                    if (index == serachFor.Length)                        {
                        return startPos;
                    }
                }    else      {
                    startPos = Array.IndexOf<byte>(searchWithin, serachFor[0], startPos + index);
                    if (startPos == -1)      {
                       return -1;
                    }
                    index = 0;
                }
            }
        }
        return -1;
    }
    private byte[] ToByteArray(Stream stream)        {
        byte[] buffer = new byte[32768];
        using (MemoryStream ms = new MemoryStream())           {
            while (true)       {
                int read = stream.Read(buffer, 0, buffer.Length);
                if (read <= 0)
                    return ms.ToArray();
                ms.Write(buffer, 0, read);
            }
        }
    }
    public bool Success        {
        get;
        private set;
    }
    public string ContentType        {
        get;
        private set;
    }
    public string Filename        {
        get;
        private set;
    }
    public byte[] FileContents        {
        get;
        private set;
    }
    public string Imgname        {
        get;
        private set;
    }    

Solution

  • Ok so I had solved this issue long back with some try and error basis, but since no one replied I am posting my solution.

    public class MultipartParser
    {
        public IDictionary<string, string> Parameters = new Dictionary<string, string>();
        public MultipartParser(Stream stream)
        {
            this.Parse(stream, Encoding.UTF8);
        }
        public MultipartParser(Stream stream, Encoding encoding)
        {
            this.Parse(stream, encoding);
        }
        public string getcontent(Stream stream, Encoding encoding)
        {
            // Read the stream into a byte array
            byte[] data = ToByteArray(stream);
            // Copy to a string for header parsing
            string content = encoding.GetString(data);
            string delimiter = content.Substring(0, content.IndexOf("\r\n"));
            string[] sections = content.Split(new string[] { delimiter }, StringSplitOptions.RemoveEmptyEntries);
            foreach (string s in sections)
            {
                Match nameMatch = new Regex(@"(?<=name\=\"")(.*?)(?=\"")").Match(s);
                string name = nameMatch.Value.Trim().ToLower();
                if (!string.IsNullOrWhiteSpace(name))
                {
                    int startIndex = nameMatch.Index + nameMatch.Length + "\r\n\r\n".Length;
                }
            }
            string strRet = ""; //Parameters["name"];
            return strRet;
        }
        private void Parse(Stream stream, Encoding encoding)
        {
            this.Success = false;            
            byte[] data = ToByteArray(stream);            
            string content = encoding.GetString(data);
    
            string s_no = content.Substring(content.IndexOf("s_no"), 100);                       
            string[] lines = s_no.Split(new[] { "\r\n", "\r\n\r\n" }, StringSplitOptions.None);
            this.S_NO = lines[3];            
    
            string count = content.Substring(content.IndexOf("Count"), 100);            
            string[] linescount = count.Split(new[] { "\r\n", "\r\n\r\n" }, StringSplitOptions.None);
            this.Count = linescount[3];   
    
            int delimiterEndIndex = content.IndexOf("\r\n");
            if (delimiterEndIndex > -1)
            {
                string delimiter = content.Substring(0, content.IndexOf("\r\n"));
                // Look for Content-Type
                Regex re = new Regex(@"(?<=Content\-Type:)(.*?)");
                Match contentTypeMatch = re.Match(content);
                // Look for filename
                re = new Regex(@"(?<=filename\=\"")(.*?)(?=\"")");
                Match filenameMatch = re.Match(content);
                re = new Regex(@"(?<=Content\-Length:)(.*)");
                Match contentLenMatch = re.Match(content);
    
                if (contentLenMatch.Success)
                {
                    contentLenMatch = contentLenMatch.NextMatch();
                }
                if (contentLenMatch.Success)
                {
                    contentLenMatch = contentLenMatch.NextMatch();
                }
                //re = new Regex(@"(?<=name\=\"")(.*?)(?=\"")");
                //Match nameMatch = re.Match(content);
                // Did we find the required values?
                if (contentTypeMatch.Success && filenameMatch.Success)
                {
                    // Set properties
                    this.ContentType = contentTypeMatch.Value.Trim();
                    this.Filename = filenameMatch.Value.Trim();
                    // Get the start & end indexes of the file contents
                    int startIndex = contentLenMatch.Index + contentLenMatch.Length + "\r\n".Length + 1;
                    byte[] delimiterBytes = encoding.GetBytes("\r\n" + delimiter);
                    int endIndex = IndexOf(data, delimiterBytes, startIndex);
                    int contentLength = endIndex - startIndex;
                    // Extract the file contents from the byte array
                    byte[] fileData = new byte[contentLength];
                    Buffer.BlockCopy(data, startIndex, fileData, 0, contentLength);
                    this.FileContents = fileData;
                    this.Success = true;
                }
            }
        }
        private int IndexOf(byte[] searchWithin, byte[] serachFor, int startIndex)
        {
            int index = 0;
            int startPos = Array.IndexOf(searchWithin, serachFor[0], startIndex);
            if (startPos != -1)
            {
                while ((startPos + index) < searchWithin.Length)
                {
                    if (searchWithin[startPos + index] == serachFor[index])
                    {
                        index++;
                        if (index == serachFor.Length)
                        {
                            return startPos;
                        }
                    }
                    else
                    {
                        startPos = Array.IndexOf<byte>(searchWithin, serachFor[0], startPos + index);
                        if (startPos == -1)
                        {
                            return -1;
                        }
                        index = 0;
                    }
                }
            }
            return -1;
        }
        private byte[] ToByteArray(Stream stream)
        {
            byte[] buffer = new byte[32768];
            using (MemoryStream ms = new MemoryStream())
            {
                while (true)
                {
                    int read = stream.Read(buffer, 0, buffer.Length);
                    if (read <= 0)
                        return ms.ToArray();
                    ms.Write(buffer, 0, read);
                }
            }
        }
        public bool Success
        {
            get;
            private set;
        }
        public string ContentType
        {
            get;
            private set;
        }
        public string Filename
        {
            get;
            private set;
        }
        public byte[] FileContents
        {
            get;
            private set;
        }
        public string Imgname
        {
            get;
            private set;
        }
        public string S_NO
        {
            get;
            private set;
        }
        public string Count
        {
            get;
            private set;
        }
    }
    // End of Wcf rest Service Code
    

    my Android code is as shown below using Okhttp

    public static ResultVO uploadIMGMultipart(String s_no, int noOfDocs, String filepath, String url) {
        ResultVO resultVO = new ResultVO();
        OkHttpClient client = new OkHttpClient().newBuilder()
                .retryOnConnectionFailure(false)
                .connectTimeout(10000, TimeUnit.MILLISECONDS)
                .build();
    
        RequestBody requestBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("s_no",s_no.toString())
                .addFormDataPart("Count", ""+noOfDocs)
                .addFormDataPart("filestream", new File(filepath).getName().toString(), RequestBody.create(MEDIA_TYPE_PNG, new File(filepath)))
                .build();
    
        Request request = new Request.Builder()
                .url(url)
                .addHeader("content-type", "multipart/form-data;")
                .cacheControl(CacheControl.FORCE_NETWORK)
                .post(requestBody)
                .build();
    
        Response response = null;
        try {
            response = client.newCall(request).execute();
            if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
    
            resultVO.setStatus(response.code());
            resultVO.setResult(response.body().string());
        } catch (Exception e) {
            e.printStackTrace();
        }
    
        return resultVO;
    }
    

    Call the above method in AsyncTask from your class thats it....