Omkar
Omkar

Reputation: 303

Store image to Server using WCF services and Xamarin.Forms

I'm developing an mobile application using Xamarin Forms where in an app send an image to server. For sending image we've used WCF services.

Well below is the code for Xamarin Application

using (var memoryStream = new MemoryStream())
        {
            pick.GetStream().CopyTo(memoryStream);
            pick.Dispose();
            byte[] byteImageArray = memoryStream.ToArray();
            try
            {
                var imageStream = new ByteArrayContent(byteImageArray);
                var multi = new MultipartContent();
                multi.Add(imageStream);

                var client = new HttpClient();
                var result = client.PostAsync("http://www.test.com/Services/Service.svc/SaveImage", multi).Result;

                var json = await result.Content.ReadAsStringAsync();
                var strNo = JsonConvert.DeserializeObject<string>(json);

            }
            catch (Exception ex)
            {
                await DisplayAlert("Error", ex.Message, "Ok");
            }
        }

And for WCF services

    public string SaveImage(Stream data)
        {
            byte[] byteImage = ReadFully(data);
            //Database logic to insert byte array
        }

public static byte[] ReadFully(Stream input)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                input.CopyTo(ms);
                return ms.ToArray();
            }
        }

Now with this code image is getting converted successfully and getting stored in database blob. Issue I'm facing is whenever I convert blob back to image, the image gets corrupted. When I insert image into blob with asp.net application the data length of blob is displayed as 18901 whereas while inserting same image with mobile application data length is 18987. Please help me to resolve the data length issue, or please guide easier way to store image into data base using WCF and Xamarin forms.

Upvotes: 2

Views: 1097

Answers (2)

Enrico
Enrico

Reputation: 6214

Create an WebAPI called PicturesController for example. You must use PUT verb

/// <summary>
/// Receiving an image across WebAPI
/// </summary>
/// <returns></returns>
[HttpPut]
public HttpResponseMessage Put()
{
    var result = new HttpResponseMessage(HttpStatusCode.OK);

    if (Request.Content.IsMimeMultipartContent())
    {
        try
        {
            Request.Content.LoadIntoBufferAsync().Wait();
            Request.Content.ReadAsMultipartAsync<MultipartMemoryStreamProvider>(
                new MultipartMemoryStreamProvider()).ContinueWith((task) => {
                MultipartMemoryStreamProvider provider = task.Result;
                foreach (HttpContent content in provider.Contents)
                {
                    Stream stream = content.ReadAsStreamAsync().Result;
                    Image image = Image.FromStream(stream);

                    try
                    {
                        string filename = string.Format("{0}{1}{2}{3}", 
                                                        DateTime.Now.Year, 
                                                        DateTime.Now.Month, 
                                                        DateTime.Now.Day, 
                                                        DateTime.Now.Second) + ".jpg";
                        foreach (var h in content.Headers.ContentDisposition.Parameters)
                        {
                            if (h.Name.ToLower() == "filename")
                            {
                                filename = h.Value.Replace("\\", "/").Replace("\"", "");
                                var pos = filename.LastIndexOf("/");
                                if (pos >= 0)
                                {
                                    filename = filename.Substring(pos + 1);
                                }
                                break;
                            }
                        }

                        string filePath = ConfigurationManager.AppSettings["Pictures"]
                                                              .ToString();
                        string fullPath = Path.Combine(filePath, filename);

                        EncoderParameters encparams = new EncoderParameters(1);
                        encparams.Param[0] = new EncoderParameter(Encoder.Quality, 80L);
                        ImageCodecInfo ici = null;
                        foreach (ImageCodecInfo codec in ImageCodecInfo
                                                         .GetImageEncoders())
                        {
                            if (codec.MimeType == "image/jpeg")
                            {
                                ici = codec;
                                break;
                            }
                        }

                        image.JpegOrientation().Save(fullPath, ici, encparams);
                    }
                    catch (Exception ex)
                    {
                    }
                }
            });
        }
        catch (Exception ex)
        {
            result.StatusCode = HttpStatusCode.InternalServerError;
        }

        return result;
    }
    else
    {
        throw new HttpResponseException(Request.CreateResponse(
                                        HttpStatusCode.NotAcceptable, 
                                        "This request is not properly formatted"));
    }
}

In this code I create a temporary file name. If you pass one as header parameter, I use that. I save the image in a folder Pictures and I read this folder from web.config. The file is in jpeg format because usually this is the image format on your device.

When you do that, you have to create a webclient in your Xamarin project.

/// <summary>
/// Uploads the photo.
/// </summary>
/// <returns>The photo.</returns>
/// <param name="photoBytes">Photo bytes.</param>
public async Task<bool> UploadPhoto(byte[] photoBytes, int PropertyId, string fileName)
{
    bool rtn = false;

    var content = new MultipartFormDataContent();
    var fileContent = new ByteArrayContent(photoBytes);
    fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
    fileContent.Headers.ContentDisposition = 
                         new ContentDispositionHeaderValue("attachment") {
        FileName = fileName + ".jpg"
    };
    content.Add(fileContent);
    fileContent.Headers.ContentDisposition.Parameters.Add(
                         new NameValueHeaderValue("<otherParam>", "<otherParamValue>"));

    string url = RestURL() + "Pictures/Put";
    try
    {
        using (var client = new HttpClient())
        {
            // add an authotization token if you have one
            //client.DefaultRequestHeaders.Add("authenticationToken", "yourToken");
            await client.PutAsync(url, content);
            rtn = true;
        }
    }
    catch (Exception ex)
    {
    }

    return rtn;
}

Remember to include

using System.Net.Http;
using System.Net.Http.Headers;

I'm using this implementation in a lot of apps and it's working perfectly. If you have any suggestion to improve it, tell me please.

Upvotes: 1

Omkar
Omkar

Reputation: 303

Simply changing

var multi = new MultipartContent();
multi.Add(imageStream);

To

StreamContent scontent = new StreamContent(pick.GetStream());
HttpContent hp = scontent;

Resolved the issue. Hope I'm not going wrong anywhere.

Upvotes: 0

Related Questions