Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Sending Stream to ServiceStack

4.75/5 (4 votes)
4 Dec 2012CPOL2 min read 45.8K   307  
Sending a stream to ServiceStack

Introduction

Recently, I needed to make a ServiceStack service which can receive big files, so I wanted to use streaming to accomplish this. Unfortunately, there isn't much information about using streams with ServiceStack, so I decided to share my experience.

We'll create a sample solution containing both Server and Client. We will create a class library containing the service itself and a Utility project. So here is the structure of our solution: let's continue with the implementation of the service. First of all, we'll need to install the ServiceStack nuget package in the ServiceStackStreaming.Service project:

PM> Install-Package ServiceStack

This will add the DLLs needed for ServiceStack. Now let's create the DTO:

C#
using ServiceStack.ServiceHost;

namespace ServiceStackStreaming.Service
{
    [Route("/upload/{FileName}", "POST")]
    public class UploadPackage : IRequiresRequestStream
    {
        public System.IO.Stream RequestStream { get; set; }

        public string FileName { get; set; }
    }
}

To enable Streaming support, we need to implement IRequiresRequestStream which needs a RequestStream property of type System.IO.Stream. We'll add a FileName property and include it in the Route so that we would be able to pass the uploaded file name. The next thing to do is to create the service itself:

C#
using ServiceStack.Common.Web;
using ServiceStackStreaming.Utility;
using System;
using System.IO;

namespace ServiceStackStreaming.Service
{
    public class UploadService : ServiceStack.ServiceInterface.Service
    {
        public object Post(UploadPackage request)
        {
            // hack - get the properties from the request
            if (string.IsNullOrEmpty(request.FileName))
            {
                var segments = base.Request.PathInfo.Split(new[] { '/' }, 
                	StringSplitOptions.RemoveEmptyEntries);
                request.FileName = segments[1];
            }

            string resultFile = Path.Combine(@"C:\Temp", request.FileName);
            if (File.Exists(resultFile))
            {
                File.Delete(resultFile);
            }
            using (FileStream file = File.Create(resultFile))
            {
                request.RequestStream.Copy(file);
            }
            
            return new HttpResult(System.Net.HttpStatusCode.OK);
        }
    }
}

Our dummy service will save the incoming file in the "C:\Temp" directory. With the code from line 12 to line 17, we are getting the FileName property if it's not set. It seems that when using streaming, the additional properties are not processed and they are always null, so we'll do this little hack to get the properties parsing the request URL. The other trick we use here is the extension method of the System.IO.Stream class which we have implemented in the ServiceStackStreaming.Utility project:

C#
using System.IO;

namespace ServiceStackStreaming.Utility
{
    public static class StreamExtender
    {
        public static void Copy(this Stream instance, Stream target)
        {
            int bytesRead = 0;
            int bufSize = copyBuf.Length;

            while ((bytesRead = instance.Read(copyBuf, 0, bufSize)) > 0)
            {
                target.Write(copyBuf, 0, bytesRead);
            }
        }
        private static readonly byte[] copyBuf = new byte[0x1000];
    }
} 

This simply copies the instance stream to the target stream. Actually we can use Service Stack StreamExtensions  WriteTo method instead.

The last thing we need to do to create a functional service is to add the AppHost class, we will inherit  AppHostHttpListenerBase as we want to host the service in a window console application.  

C#
using ServiceStack.WebHost.Endpoints;

namespace ServiceStackStreaming.Service
{
    public class AppHost : AppHostHttpListenerBase
    {
        public AppHost() : base("Agent", typeof(UploadService).Assembly) { }

        public override void Configure(Funq.Container container)
        {
            // we can add the routing here instead of adding it as attribute to the DTO
            //Routes
            //    .Add<uploadpackage>("/upload/{FileName}", "POST");
        }
    }
}

We can configure the route here, but I prefer doing this with attribute. Now let's host the service. To do this, we'll need to add the same ServiceStack nuget to the SertviceStackStreaming.Server project and add the following code to the Program.cs file:

C#
using ServiceStackStreaming.Service;
using System;

namespace ServiceStackStreaming.Server
{
    class Program
    {
        static void Main(string[] args)
        {
            var appHost = new AppHost();
            appHost.Init();
            appHost.Start("http://*:1999/");

            Console.WriteLine("Service listening on port 1999!");
            Console.ReadKey();
        }
    }
}

This will be enough to host the service listening to port 1999. Now let's call the service from the ServiceStackStreaming.Client (again, we'll have to install the same nuget package here).

C#
using ServiceStackStreaming.Utility;
using System.IO;
using System.Net;

namespace ServiceStackStreaming.Client
{
    class Program
    {
        static void Main(string[] args)
        {
            string filePath = @"c:\temp\upload.zip";
            
            HttpWebRequest client = (HttpWebRequest)WebRequest.Create
            	("http://localhost:1999/upload/upload-copy.zip");
            client.Method = WebRequestMethods.Http.Post;
            
            // the following 4 rows enable streaming 
            client.AllowWriteStreamBuffering = false;
            client.SendChunked = true;
            client.ContentType = "multipart/form-data;";
            client.Timeout = int.MaxValue;

            using (FileStream fileStream = File.OpenRead(filePath))
            {
                fileStream.Copy(client.GetRequestStream());
            }

            var response = new StreamReader
            	(client.GetResponse().GetResponseStream()).ReadToEnd();
        }
    }
}

And that's it. We create WebRequest, set the needed properties to enable streaming on the client and copy the file stream to the request stream. This will call the service and will upload the "C:\Temp\upload.zip" file as upload-copy.zip file.

You can find the sample code here.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)