WCF streaming files - wcf

I need to pass a memory stream to the WCF server , how do i need to add this data type in my data contract.
I will eventually need to convert this to a memory stream and pass it on to my service layer.
datacontact[DataMember]
Stream str = null;
public Stream File
{
get { return str; }
set { str = value; }
}

Here is the WCF Streaming page. I'm not really sure if (how) you can do this with a DataContract, the normal way is to specify streams in the OperationContract. Wouldn't that work for you?
Short summary:
Sender produces the Stream
Sender does not Close the Stream
Receiver does close the stream
Set the MaxReceivedMessageSize property of the binding to a value larger than the largest item you wish to transfer.

Related

Restful WCF Service - returning byte[]?

I'm working with a RESTful WCF service. One of the service methods returns byte[] (which contains a file).
On the client side, I have some code that uses the WebRequest class to invoke that particular service method. Using the WebRequest, I'm getting the response stream like:
Stream stream = webReq.GetResponse().GetResponseStream();
From this stream, I am then reconstructing a byte[] and then outputting the File locally. The problem is that the reconstructed file on the client-side doesn't resemble the file that was returned from the service side (I get a corrupt PDF file that is much larger in size than the one sent from the service side). Just before the service method returns the byte[], I outputted that byte[] to disk on the service side and it created the file fine... This points to something going wrong between that method returning the byte[] and my client side reconstructing the byte[] from a Stream on the client side... to reconstruct the byte[] from the Stream, I'm using the following method which someone posted in the past on stackoverflow:
public static byte[] ReadFully(Stream input)
{
byte[] buffer = new byte[16*1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
Any ideas what could be going wrong?
I guess that the response from the server contains some envelope in addition to the raw bytes. Like a XML envelope or something. Which would of course suppose that the bytes are base64 encoded string in the response because you cannot store binary data into XML. It would also explain why you are getting a bigger buffer on the client than the actual PDF that the server has sent.
This will of course depend on what binding your WCF service uses and how is it configured. When you dump the contents of the MemoryStream you are reading on the client what exactly do you see? This should give you further hints on how the actual PDF file is encoded in the HTTP response body.

creating file from returned byte array via WCF Rest Service

I am working on creating a WCF rest service. I have a function that will return a byte array of a file created by the service. I was wondering how I can create this file, from the byte array on the client side. This is in C#.
Simply write all received bytes to the file:
File.WriteAllBytes("filename.bin", receivedBytesArray);
Save stream to file:
Stream receivedStream = // do some staff to receive stream
using (FileStream stream = File.OpenWrite("myfilepath.bin"))
{
receivedStream.CopyTo(stream);
}

resuming files when uploading to a server using wcf

I'm using WCF and I am trying to resume my upload with next code on the server app:
class DataUploader : IDataUploader
{
public void Upload(UploadMessage msg)
{
int speed = msg.AvgSpeed * 1024; // convert to KB
Stream stream= msg.DataStream;
string name = msg.VirtualPath;
int seekPoint; // this is get reading the partial uploaded file
using (FileStream fs = new FileStream(#"C:\savedfile.dat, FileMode.Append))
{
int bufferSize = 4 * 1024; // 4KB buffer
byte[] buffer = new byte[bufferSize];
int bytes;
while ((bytes = stream.Read(buffer, startPoint, bufferSize)) > 0)
{
fs.Write(buffer, 0, bytes);
fs.Flush();
}
stream.Close();
fs.Close();
}
}
}
I'm trying to begin to read the stream from a specified point (startPoint) cause the first bytes have already been uploaded. So I could append only remaining bytes to the file partially uploaded. By this way i get an error with the buffersize and can't use seeking because a method not supported exception so I think maybe this approach is not right. Help!!
My service contract:
[ServiceContract]
interface IDataUploader
{
[OperationContract]
void Upload(UploadMessage msg);
}
My message contract:
[MessageContract]
public class UploadMessage
{
[MessageHeader(MustUnderstand = true)]
public string VirtualPath { get; set; }
[MessageHeader(MustUnderstand = true)]
public int AvgSpeed { get; set; }
[MessageBodyMember(Order = 1)]
public Stream DataStream { get; set; }
}
It seems like you are using a standard soap message rather than the streaming binding. Check out the this link
If you don't want to use WCF's streaming api, which is proprietary to WCF, I would considering creating a 'chunking' method from the client if the client is uploading the file. Similar to how FTP can resume, I would query the server to see the current offset, send up a block or set of blocks, write them to my persistance (memory, db, file, etc), and then continue with multiple calls from the client sending smaller blocks (be careful of serialization as that can introduce unnecessary delays). This technique be something you want to investigate since it sounds like the client is 'streaming' to the server.
Btw, you may want to look at the following article to determine if your use of MessageContract is appropriate, as opposed to a DataContract.
http://blogs.msdn.com/b/drnick/archive/2007/07/25/data-contract-and-message-contract.aspx
If you want resume functionality you cannot do it this way. Your client must send the file in chunks and it must maintain the id of last successfully updated chunk. The service must process chunks and append them to storage.
If the most basic implementation it means that your client must divide file into chunks of well known size and call the upload operation for each chunk. The message must also contains the chunk Id and probably also chunk size (or something identifying the last chunk). This can be also combined with reliable session to allow automatic resend of lost chunks and to enforce in order delivery.
There is also example of channel implementation which does chunking internally.

Exposing object of unknown type from a WCF service

[OperationContract]
public object GetDeserializedObject(int partyContactID)
{
PartyContact partyContact = GetPartyContactById(partyContactID);
ContactTermResultQueue resultQueue = GetContactTermResultQueueByID(partyContact.TemplateQueueID);
byte[] contactDataSetArray = resultQueue.QueryResult;
//Getting DataSet from the byte array
BinaryFormatter binaryFormatter = new BinaryFormatter();
Stream mStreamtoRead = new MemoryStream(contactDataSetArray);
object o = binaryFormatter.Deserialize(mStreamtoRead);
mStreamtoRead.Close();
object returnData=null;
if (o.GetType().IsArray)
{
object[] os = o as object[];
var value = from vs in os where (int) (vs.GetType().GetProperty("PartyID").GetValue(vs, null)) == partyContact.PartyID select vs;
if (value.Count() > 0)
{
returnData = value.First();
}
}
return returnData;
}
As I don't know what type of data we are going to have in the database, so wanted to return the object from this service, but it is giving me an exception.
Please let me know how can I achieve this?
Thanks in advance
You can't return object and expect it will work. The reason is that behind this code WCF engine uses serialization. When client receives message it must be able to deserialize it back to some object but to be able to do that it must know what type of object it received.
If you want to send "unknown" data use XElement. Client will receive just XML and it will be its responsibility to deal with it (parse it, deserialize it or whatever).
You can do certain things with the "raw" Message data type - but it's really not pretty programming...
Read about it here:
How to pass arbitrary data in a Message object using WCF
WCF : Untyped messages on WCF operations.

Silverlight changes the io.Stream to byte[]

I have created a WCF service for uploading images , which accepts System.IO.Stream as input parameter and am using streaming. When I added the service reference in Silverlight project then it automatically changed the parameter of my WCF method from System.IO.Stream to byte[]. Can anyone suggest if there is a way around this so that I can get System.IO.Stream type rather than byte[].
Thanks in advance
Silverlight does not support transfer mode streamed: http://forums.silverlight.net/forums/t/119340.aspx
So I think that you are stuck with getting a byte array.
Can you verify that you're not hitting one of the reader quotas in the service? You can try increasing all of them to see if this solves your problem.
I think you should set the transferMode property of your basicHttpBinding to the correct value, as described in this article. And then add the service reference to your Silverlight application again.
http://blogs.msdn.com/b/carlosfigueira/archive/2010/07/08/using-transfermode-streamedresponse-to-download-files-in-silverlight-4.aspx
Even I was struggling with the same issue. At last I got a solution by myself. All you can do is:
declare the accepting parameter as string array in the WCF Service.
convert the byte array into string array at client place.
After Sending the converted byte array as string array again convert back it into byte array.
eg. at the WCF side:
[DataContract]
Class FileInfo
{
[DataMember]
string filename;
[DataMember]
string[] StrArr;
}
the receiving function:
public void uploadFile(FileInfo fi)
{
int len=fi.StrArr.len;
byte[] myFileByte=new byte[len];
for(int i=0;i<len;i++)
{
myFileByte[i]=Convert.ToByte(fi.StrArr[i]);
}
//your uploaded File buffer is ready as myFileByte
//proceeding operations are most welcome here......
.........
}
At Client Side:
public void UploadMyFile()
{
//Take the InputStream from the selected File as iStream;
int len=(int)iStream.length;
byte[] buffer=new byte[len];
string[] MyStrArr=new string[len];
for(int i=0;i<len;i++)
{
MyStrArr[i]=Convert.ToString(buffer[i]);
}
//Here your string array is ready to send to the WCF Service....
//I m confident this code will work perfectly with some file limitation consideartions.
}