none
Upload binary data to WCF REST Service

    Question

  • Hi,

    I have a simple WCF REST Service I am testing:

    This works fine:

        [OperationContract]
        [WebGet(UriTemplate = "users({personId})/picture")]
        Stream GetImage(string personId);

    With test implementation:

        public Stream GetImage(string personId)
        {
            FileStream stream = File.OpenRead(@"c:\Labs\DL.jpg");
            WebOperationContext.Current.OutgoingResponse.ContentType = "image/jpeg";
            return stream as Stream;
        }

    Fiddler or a web browser will show the image, no problem.

    However for uploading images I am having more problems.  Here is the simple operation:

        [OperationContract]
        [WebInvoke(Method="PUT", UriTemplate = "users({personId})/picture")]
        string UploadImage(string personId,Stream image);

    And the implementation:

           public string UploadImage(string personId,Stream image)
            {
                return image.Length + " bytes uploaded";
               
            }

    I have a wpf .net client that is doing the following:

            WebClient client = new WebClient();
            var results = client.UploadData("http://ipv4.fiddler:30979/TestService.svc/users(1)/picture", "PUT", GetData());
            MessageBox.Show(results.Length.ToString());

    (note the ipv4.fiddler address is just so fiddler can access localhost requests)

    This results in the error: **The remote server returned an error: (415) Unsupported Media Type**

    If I add a content type header :

        WebClient client = new WebClient();
        client.Headers.Add("Content-Type", "image/jpeg");
        var results = client.UploadData("http://ipv4.fiddler:30979/TestService.svc/users(1)/picture", "PUT", GetData());
        MessageBox.Show(results.Length.ToString());

    GetData() is simply this:

     

    private static byte[] GetData()

    {

     

    FileStream stream = File.OpenRead(@"c:\Labs\DLe.jpg");

     

    byte[] data = new byte[stream.Length];

    stream.Read(data, 0, data.Length);

    stream.Close();

     

    return data;

    }

     



    I get **The remote server returned an error: (400) Bad Request.**

    I suspect that either:

    1) I have the wrong content type - in which case what should I use?
    2) I should be using a byte[] instead of a stream for the service operation - but I currently get the same issues with that
    3) I am way off track and should be doing something completely different!

    Any help much appreciated...

    Cheers

    Ian


    Ian
    Tuesday, September 08, 2009 11:07 PM

Answers

  • The stream passed as the parameter is not seekable, so you cannot call the Length property on it. To get the size of the string you can either get the content-length of the request:

     

    public string UploadImage(string personId, Stream image)
    {
        return WebOperationContext.Current.IncomingRequest.ContentLength + " bytes uploaded";
    }

     


    or you can read the string yourself:

     

    public string UploadImage(string personId, Stream image)
    {
        int bytesRead, totalBytesRead = 0;
        byte[] buffer = new byte[1000];
        do
        {
            bytesRead = image.Read(buffer, 0, buffer.Length);
            totalBytesRead += bytesRead;
        }
    while (bytesRead > 0);
        return totalBytesRead + " bytes uploaded";
    }

    • Marked as answer by IanBlackburn Tuesday, September 08, 2009 11:38 PM
    Tuesday, September 08, 2009 11:20 PM

All replies

  • The stream passed as the parameter is not seekable, so you cannot call the Length property on it. To get the size of the string you can either get the content-length of the request:

     

    public string UploadImage(string personId, Stream image)
    {
        return WebOperationContext.Current.IncomingRequest.ContentLength + " bytes uploaded";
    }

     


    or you can read the string yourself:

     

    public string UploadImage(string personId, Stream image)
    {
        int bytesRead, totalBytesRead = 0;
        byte[] buffer = new byte[1000];
        do
        {
            bytesRead = image.Read(buffer, 0, buffer.Length);
            totalBytesRead += bytesRead;
        }
    while (bytesRead > 0);
        return totalBytesRead + " bytes uploaded";
    }

    • Marked as answer by IanBlackburn Tuesday, September 08, 2009 11:38 PM
    Tuesday, September 08, 2009 11:20 PM
  • Many thanks Carlos - that fixed it! - I was looking at the client code as the problem - completely ignoring that.
    Ian
    Tuesday, September 08, 2009 11:39 PM
  • I am still getting the 400 bad request error.

    my client looks like this,

    FileStream stream = File.OpenRead(@"c:\BK2.jpg");
    byte[] arr = new byte[stream.Length];
    stream.Read(arr, 0, System.Convert.ToInt32(stream.Length));
    stream.Close();
    
    WebClient client = new WebClient();
    client.Headers.Add("Content-Type", "image/jpeg");
    var results = client.UploadData("http://localhost:2981/Service1.svc/Upimage/10", "PUT", arr);
    
    MessageBox.Show(results.Length.ToString());
    Console.WriteLine("the size of the file{0}", results.Length.ToString());
    
     The host service is 

    [OperationContract]
    [WebInvoke(Method="PUT",UriTemplate="Upimage/{path}")]
    string UpImage(string path,Stream filecontents);
    
    
    
    
    
     public string UpImage(string path,Stream filecontents)
       {        
                return WebOperationContext.Current.IncomingRequest.ContentLength + " bytes uploaded";
            }
    
    .. 
    Any help will be very appreciated!
    Thursday, November 05, 2009 12:10 AM
  • Ian, can you post your working solution? I am still getting 415 error. Can you also post your web.config?
    Friday, February 26, 2010 11:02 PM
  • Hi MertS

    This is the code in my service:
            [OperationContract]
            [WebInvoke(Method = "POST", UriTemplate = "reports/{emailAddress}/{deviceUUID=null}")]
            void AddReport(string emailAddress, string deviceUUID, Stream item);
    I then deserialize the stream into an object (the serviceserializer is my own type inherited from DataContractSerializer) and pass it to my repository to do the db work

       public void AddReport(string emailAddress, string deviceUUID, Stream item)
            {
                ClientReportItem clientReportItem = ServiceSerializer<ClientReportItem>.Deserialize(item);
                MobileReportsRepository rep = new MobileReportsRepository();
                var returnstatus= rep.AddReport(emailAddress, deviceUUID, clientReportItem);
    On the client side I do something like the following: construct the type I want (ImageData is a byte array), serialize it to a stream and then use WebClient (I am writing the serialized data to file here but you could use a MemoryStream I think) to upload

    ClientReportItem item = new ClientReportItem()
                {
                    DateTimeRecorded = DateTime.Now,
                    Description = "Large hole in footway outside 85 Roberts Road, E17",
                    ImageUrl = null,
                    ImageData = GetImage("1"),
                    Latitude = 51.4623497706898,
                    Longitude = -0.011200904846226493,
                    ResponseRequired = true,
                    Tags = new string[] {},
                    CategoryId=1,
                    StatusId=1
                };
                DataContractSerializer serializer = new DataContractSerializer(typeof(ClientReportItem));
                XmlWriter writer = XmlWriter.Create(@"c:\labs\item3.xml");
                serializer.WriteObject(writer, item);
                writer.Close();
    
                using (WebClient client = new WebClient())
                {
                    client.UploadFile("http://localhost/LCSAPI/mobile/reports/447973622180@mediaklik.com/MMS", "POST", @"c:\labs\item3.xml");
                   
                }
    Config on the service is minimal

     <service behaviorConfiguration="debugBehaviour" name="LCSAPI.Mobile">
            <endpoint address="" binding="webHttpBinding" bindingConfiguration="webBindingConfig"
              contract="LCSAPI.IMobile" />
          </service>
     <webHttpBinding>
            <binding name="webBindingConfig" maxBufferSize="5242880" maxReceivedMessageSize="5242880">
              <readerQuotas maxStringContentLength="5242880" maxArrayLength="5242880" />
              <security>
                <transport>
                  <extendedProtectionPolicy policyEnforcement="Never" />
                </transport>
              </security>
            </binding>
          </webHttpBinding>
    and the svc markup:

    <%@ ServiceHost Language="C#" Debug="true" Service="LCSAPI.Mobile" CodeBehind="Mobile.svc.cs"
     Factory="System.ServiceModel.Activation.WebServiceHostFactory" %>
    

    Hope that helps.

    Ian


    Ian
    Saturday, February 27, 2010 9:49 AM