4

Possible Duplicate:
WCF Rest Webservice with stream

I'm developing a WCF .NET Framework 4.0 with C#.

I've created this WCF with this Visual Studio Template:

enter image description here

I need to send an image with two or three parameters. This is all OperationContract that I have (I'm asking for the last one):

[ServiceContract]
public interface IRestServiceImpl
{
    [OperationContract]
    [WebInvoke(Method = "GET",
        ResponseFormat = WebMessageFormat.Json,
        BodyStyle = WebMessageBodyStyle.Bare,
        UriTemplate = "orders/")]
    OrderContract[] allOrders();

    [OperationContract]
    [WebInvoke(Method = "POST",
        ResponseFormat = WebMessageFormat.Json,
        BodyStyle = WebMessageBodyStyle.Bare,
        UriTemplate = "filteredOrders/")]
    OrderContract[] GetOrders(IdsMessage msg);

    [OperationContract]
    [WebInvoke(Method = "POST",
        ResponseFormat = WebMessageFormat.Json,
        BodyStyle = WebMessageBodyStyle.Bare,
        UriTemplate = "completeFilteredOrders/")]
    OrderContract[] LoadCompleteFilteredOrders(IdsMessage msg);

    [OperationContract]
    [WebInvoke(Method = "POST",
        ResponseFormat = WebMessageFormat.Json,
        BodyStyle = WebMessageBodyStyle.Bare,
        UriTemplate = "saveEReports/")]
    Boolean SaveEReports(EReportContract[] eReports);

    [OperationContract]
    [WebInvoke(Method = "POST",
        ResponseFormat = WebMessageFormat.Json,
        BodyStyle = WebMessageBodyStyle.Bare,
        UriTemplate = "saveEReport/")]
    long SaveEReport(EReportContract eReport);

    [OperationContract]
    [WebInvoke(Method = "POST",
        ResponseFormat = WebMessageFormat.Json,
        BodyStyle = WebMessageBodyStyle.Bare,
        UriTemplate = "UploadPhoto/{eReportId}/{imageType}")]
    Boolean UploadPhoto(string eReportId, string imageType, Stream fileContents);
}

And this is Web.config:

<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <services>
      <service name="EReportService.RestServiceImpl" behaviorConfiguration="ServiceBehaviour">
        <endpoint address="" binding="webHttpBinding" contract="EReportService.IRestServiceImpl" behaviorConfiguration="web">
        </endpoint>
      </service>
    </services>
    <bindings>
      <webHttpBinding>
        <binding maxReceivedMessageSize="2097152" maxBufferSize="2097152" transferMode="Streamed"/>
      </webHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="ServiceBehaviour">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="web">
          <webHttp/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
 <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>
  <connectionStrings>

  </connectionStrings>
</configuration>

When I run service I get the following exception:

The operation must have a single parameter whose type is Stream

If I do this:

[OperationContract]
[WebInvoke(Method = "POST",
    ResponseFormat = WebMessageFormat.Json,
    BodyStyle = WebMessageBodyStyle.Bare,
    UriTemplate = "UploadPhoto")]
Boolean UploadPhoto(Stream fileContents);

It works perfectly but I need to send more data with image.

This service is exposed for an Android tablet application. The following code show how I'm sending now images to server:

public static Boolean sendImage(String url, String filePath)
{
    try
    {
        MultiValueMap<String, Object> formData;

        Resource resource = new FileSystemResource(filePath);

        // populate the data to post
        formData = new LinkedMultiValueMap<String, Object>();
        formData.add(OrderSpringController.FILE, resource);

        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setAccept(Collections.singletonList(new MediaType("application","json")));

        // Sending multipart/form-data
        requestHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);

        // Populate the MultiValueMap being serialized and headers in an HttpEntity object to use for the request
        HttpEntity<MultiValueMap<String, Object>> requestEntity = 
                new HttpEntity<MultiValueMap<String, Object>>(formData, requestHeaders);

        GsonHttpMessageConverter messageConverter = new GsonHttpMessageConverter();
        List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
        messageConverters.add(messageConverter);

        // Create a new RestTemplate instance
        RestTemplate restTemplate = new RestTemplate(true);
        restTemplate.getMessageConverters().add(messageConverter);

        // Make the network request, posting the message, expecting a String in response from the server
        ResponseEntity<Boolean> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity,
                Boolean.class);

        // Return the response body to display to the user
        return response.getBody();
    }
    catch (Exception ex)
    {
        ex.printStackTrace();
    }
    return null;
}

The error occurs when browsing to metadata endpoint (http://localhost:2351/RestServiceImpl.svc).

How can I send an image and parameters?

Community
  • 1
  • 1
VansFannel
  • 45,055
  • 107
  • 359
  • 626

3 Answers3

5

If you switch to (BasicHttp or WSHttp )Binding then you can achieve your goal by composing a single data type including all your custom parameters via properties and the Stream object itself. Then you are making use of messaging style instead of RPC conversation. And pls pay attention to [MessageContract] family of WCF DTO decorators.

[ServiceContract]
public interface ITransferService
{
    [OperationContract]
    RemoteFileInfo DownloadFile(DownloadRequest request);

    [OperationContract]
    void UploadFile(RemoteFileInfo request); 
}
[MessageContract]
public class DownloadRequest
{
    [MessageBodyMember]
    public string FileName;
}

[MessageContract]
public class RemoteFileInfo : IDisposable
{
    [MessageHeader(MustUnderstand = true)]
    public string FileName;

[MessageHeader(MustUnderstand = true)]
public long Length;

[MessageBodyMember]
public System.IO.Stream FileByteStream;

public void Dispose()
{ 
    if (FileByteStream != null)
    {
        FileByteStream.Close();
        FileByteStream = null;
    }
}   

}

This has necessary code http://www.codeproject.com/Articles/166763/WCF-Streaming-Upload-Download-Files-Over-HTTP

But I strongly suggest WCF: using streaming with Message Contracts and http://blogs.msdn.com/b/carlosfigueira/archive/2011/03/25/wcf-streaming-inside-data-contracts.aspx

This is a MUST, as well :)

Community
  • 1
  • 1
Arman
  • 5,136
  • 3
  • 34
  • 36
  • I get this error: `Operation 'UploadPhoto' contract 'IRestServiceImpl' uses a MessageContract that has SOAP headers. MessageVersion not handle SOAP headers.` I have changed this: `Boolean UploadPhoto(PhotoUploadMessage photoMessage);`. – VansFannel Nov 28 '12 at 11:10
  • Seems like you didn't switch to BasicHttpBinding and still programming against REST (WebHttpBinding). If you change your configuration and refactor the service a bit this should work. Speaking generally, REST is not the best way of streaming over internet. Why don't you switch to RPC (SOAP, BasicHttp)? – Arman Nov 28 '12 at 11:27
  • Pls CHeck out [this](http://code.msdn.microsoft.com/windowsdesktop/Upload-files-using-a-REST-13f16af2) and this http://stackoverflow.com/questions/6668246/wcf-rest-file-upload. But anyway you will have to create the container contract object. I recommend switching to BasicHttpBinding for streaming purpose. – Arman Nov 28 '12 at 11:33
  • Be also careful with your UriTemplate if you choose to stay with REST. [here](http://stackoverflow.com/questions/664712/restful-wcf-service-image-upload-problem) is a solution that worked. – Arman Nov 28 '12 at 11:39
  • I have more `[OperationContract]` on Web Service and they are RESTfull so, may I need to create a new `IRestServiceImpl.svc` to use `BasicHttpBinding`? – VansFannel Nov 28 '12 at 17:18
  • Pls update your question body with the list of all the operation contracts within that RESTful service, I need to look at the service factoring to answer this question correctly. Then I'll reply you with the steps you need to go through :). And also don't forget to mention what clients are you exposing the service for? (asp.net, php, java, mobile/desktop etc.) – Arman Nov 28 '12 at 21:13
  • I've updated my question with more details and code. If you need something else, please tell me. Thank you very much for your help. – VansFannel Nov 29 '12 at 07:27
  • I think I can send another data as part as `formData`like this: `formData.add("eReportId", eReportId);`. What do you think? – VansFannel Nov 30 '12 at 08:17
  • Hey :) I was able to done exactly what you need and it works. I'll be posting some key concepts today a bit later, and will send you the zipped project to your email from your website. – Arman Nov 30 '12 at 10:03
  • The error occurs when browsing to metadata endpoint (http://localhost:2351/RestServiceImpl.svc). – VansFannel Dec 02 '12 at 18:47
  • I believe `Order = 1` is redundant as you only have one body element – Ohad Schneider May 11 '14 at 17:29
  • @OhadSchneider you're right, I removed the Order property. – Arman May 12 '14 at 09:05
1

Try reordering your parameters so that the Stream ist the last one.

Also have a look here

Lukas Winzenried
  • 1,919
  • 1
  • 14
  • 22
1

I have created new type and selalize it

it contains the 3 values ( stream , reportid ,reporttype )

below is the code

namespace WcfService5
{

    [ServiceContract]
    public interface IService1
    {    
        [OperationContract]
        Boolean UploadPhoto(data fileContents);
    }

    [Serializable]
    public struct data 
    {
        System.IO.Stream mystream;
        string ereport;
        string reporttype;
    }
}

here is the service code

public class Service1 : IService1
{
    public bool UploadPhoto( data value)
    {
        return false;
    }     
}
Tolga Evcimen
  • 7,112
  • 11
  • 58
  • 91
Athamneh
  • 298
  • 1
  • 9