Click here to Skip to main content
15,867,330 members
Articles / Programming Languages / XML
Article

WCF Streaming: Upload/Download Files Over HTTP

Rate me:
Please Sign up or sign in to vote.
4.82/5 (60 votes)
9 Mar 2011CPOL3 min read 592.9K   34.3K   168   90
A sample for uploading/downloading large files from a browser to a WCF service via a web server.

Wordley_ScreenShot.jpg

Purpose of this article

I have tried to transfer large files over HTTP to/from WCF, but I have faced problems in that I was not able to upload files more than 45 KB in size. I have Googled over www, but I did not find any ready-to-use sample code/solution. Using the explanations over the www and MSDN, I have tried many configuration setting combinations and finally succeeded in transferring large files (I have tested up to 1GB on IE6).

I would like to share my experience so as to support efforts of others in this direction and to invite review comments from the developer community.

Explanation

To transfer large files using “WCF service + HTTP”, we can use the following types of bindings:

  1. wsHttpBinding
  2. basicHttpBinding

In wsHttpBinding, we can set the transfermode attribute as Buffered, but there is a disadvantage in using this approach for large files, because it needs to put the entire file in memory before uploading/downloading, A large buffer is required on both the web client and the WCF service host. However, this approach is very useful for transferring small files, securely.

In basicHTTPBinding we can use the transfermode as Streamed so that the file can be transferred in the form of chunks. We have to ensure additional security mechanisms for transferring chunks. The security mechanisms are not explained in this posting.

Implementation: WCF Service

Create a new “WCF Service” project. Create a new service with the name TransferService. Now we will see an interface file “ITransferService” and a class file TransferService.cs. ITransferService should have two methods, one for upload and one for download.

WCF Service Sample Interface Code:
C#
[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(Order = 1)]
    public System.IO.Stream FileByteStream;

    public void Dispose()
    { 
        if (FileByteStream != null)
        {
            FileByteStream.Close();
            FileByteStream = null;
        }
    }   
}
WCF Service Sample Interface Implementation Code:
C#
public RemoteFileInfo DownloadFile(DownloadRequest request)
{
    RemoteFileInfo result = new RemoteFileInfo();
    try
    {
        string filePath = System.IO.Path.Combine(@"c:\Uploadfiles", request.FileName);
        System.IO.FileInfo fileInfo = new System.IO.FileInfo(filePath); 

        // check if exists
        if (!fileInfo.Exists)
            throw new System.IO.FileNotFoundException("File not found", 
                                                      request.FileName);

        // open stream
        System.IO.FileStream stream = new System.IO.FileStream(filePath, 
                  System.IO.FileMode.Open, System.IO.FileAccess.Read);

            // return result 
            result.FileName = request.FileName;
            result.Length = fileInfo.Length;
            result.FileByteStream = stream;
        }
        catch (Exception ex)
        {

        }
        return result; 
    }
    public void UploadFile(RemoteFileInfo request)
    {
        FileStream targetStream = null;
        Stream sourceStream =  request.FileByteStream;

        string uploadFolder = @"C:\upload\";
         
        string filePath = Path.Combine(uploadFolder, request.FileName);

        using (targetStream = new FileStream(filePath, FileMode.Create, 
                              FileAccess.Write, FileShare.None))
        {
            //read from the input stream in 65000 byte chunks
            
            const int bufferLen = 65000;
            byte[] buffer = new byte[bufferLen];
            int count = 0;
            while ((count = sourceStream.Read(buffer, 0, bufferLen)) > 0)
            {
                // save to output stream
                targetStream.Write(buffer, 0, count);
            }
            targetStream.Close();
            sourceStream.Close();
        }

    }

Settings of “Web.Config” in the WCF Service

The following settings are most important for transferring large data:

  • ReaderQuotas: We have to set the maximum sizes (this depends on our specific requirement). Here I have set to the maximum values where we can transfer data up to 2GB, as per MSDN/www documentation.
  • XML
    <binding name="TransferService"
           maxReceivedMessageSize="2147483647"
           maxBufferSize="2147483647" transferMode="Streamed" >
                    
    <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" 
         maxArrayLength="2147483647" maxBytesPerRead="2147483647" 
         maxNameTableCharCount="2147483647"/>

    A word about my experiences here: I have used the above settings alone and received the error “400 bad request” from the WCF service on the browser.

    This error may occur due to many reasons: the reasons might be configuration mismatch between the web server and the WCF service, or an exception raised in the WCF service etc.

  • BindingConfiguration: This attribute is not available by default in the endpoint section. When I added this attribute, the “400 bad request” exception disappeared and the upload and download executed well.
  • XML
    <endpoint address="" binding="basicHttpBinding" 
              bindingConfiguration="TransferService" 
              contract ="ITransferService">
    </endpoint>

    Along with the above settings, HttpRuntime settings should also be placed in the web.config file as below:

    XML
    <httpRuntime maxRequestLength="2097151" //Maxvalue
         useFullyQualifiedRedirectUrl="true"
         executionTimeout="14400"   />  //can be configured as per the requirement.

Implementation: Web Server

Create a new “web site” project. Place a link button and a file upload control on the web page. Create one more button to upload the file. Add the reference of the WCF service in “Service References” with a suitable name, currently FileTransferServiceReference.

The following changes need to be done in web.config after adding the service reference:

XML
<binding name="BasicHttpBinding_ITransferService" closeTimeout="04:01:00"
     openTimeout="04:01:00" receiveTimeout="04:10:00" sendTimeout="04:01:00"
     allowCookies="false" bypassProxyOnLocal="false" 
     hostNameComparisonMode="StrongWildcard"
     maxBufferSize="2147483647" maxBufferPoolSize="2147483647" 
     maxReceivedMessageSize="2147483647"
     messageEncoding="Text" textEncoding="utf-8" 
     transferMode="Streamed"
     useDefaultWebProxy="true">
  <readerQuotas maxDepth="128" 
      maxStringContentLength="2147483647" maxArrayLength="2147483647"
      maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
  <security mode="None">
      <transport clientCredentialType="None" 
              proxyCredentialType="None" realm="" />
      <message clientCredentialType="UserName" algorithmSuite="Default" />
  </security>
</binding>

Increase the values for the readerQuotas attribute and also increase the timeout settings.  

The code-behind of the page is the following:

C#
protected void LinkButton1_Click(object sender, EventArgs e)
{
    try
    {
        FileTransferServiceReference.ITransferService 
                    clientDownload = new TransferServiceClient();
        FileTransferServiceReference.DownloadRequest requestData = new DownloadRequest();

        FileTransferServiceReference.RemoteFileInfo fileInfo = new RemoteFileInfo();
        requestData.FileName = "codebase.zip";
 
        fileInfo = clientDownload.DownloadFile(requestData);

        Response.BufferOutput = false;   // to prevent buffering 
        byte[] buffer = new byte[6500];
        int bytesRead = 0;

        HttpContext.Current.Response.Clear();
        HttpContext.Current.Response.ClearHeaders();
        HttpContext.Current.Response.ContentType = "application/octet-stream";
        HttpContext.Current.Response.AddHeader("Content-Disposition", 
                    "attachment; filename=" + requestData.FileName);

        bytesRead = fileInfo.FileByteStream.Read(buffer, 0, buffer.Length);

        while (bytesRead > 0)
        {
            // Verify that the client is connected.
            if (Response.IsClientConnected)
            {

                Response.OutputStream.Write(buffer, 0, bytesRead);
                // Flush the data to the HTML output.
                Response.Flush();

                buffer = new byte[6500];
                bytesRead = fileInfo.FileByteStream.Read(buffer, 0, buffer.Length);
 
            }
            else
            {
                bytesRead = -1;
            }
         }
    }
    catch (Exception ex)
    {
        // Trap the error, if any.
        System.Web.HttpContext.Current.Response.Write("Error : " + ex.Message);
    }
    finally
    {
        Response.Flush();
        Response.Close();
        Response.End();
        System.Web.HttpContext.Current.Response.Close();
    }
}

protected void Button1_Click(object sender, EventArgs e)
{ 
    if (FileUpload1.HasFile)
    {
        System.IO.FileInfo fileInfo = 
               new System.IO.FileInfo(FileUpload1.PostedFile.FileName);
        FileTransferServiceReference.ITransferService clientUpload = 
               new FileTransferServiceReference.TransferServiceClient();
        FileTransferServiceReference.RemoteFileInfo 
               uploadRequestInfo = new RemoteFileInfo();

        using (System.IO.FileStream stream = 
               new System.IO.FileStream(FileUpload1.PostedFile.FileName, 
               System.IO.FileMode.Open, System.IO.FileAccess.Read))
        {
            uploadRequestInfo.FileName = FileUpload1.FileName;
            uploadRequestInfo.Length = fileInfo.Length;
            uploadRequestInfo.FileByteStream = stream;
            clientUpload.UploadFile(uploadRequestInfo);
            //clientUpload.UploadFile(stream);
        }
    }
}

Now build both the IDE projects and execute the web server project. This still works only for less than 50KB! If you select a large file to be uploaded, you will see a blank webpage.

Now, you need to add one more attribute, MaxRequestLength, in the httpRuntime section in the system.web configuration file section, as below:

XML
<httpRuntime maxRequestLength="2097150"/>

That is it! Now one can download and upload large files (I have tested up to ~1GB) from an IE6 browser over HTTP to a WCF service.

License

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


Written By
India India
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralRe: What about Upload with a boolean return? Pin
Member 1149170820-Mar-15 4:51
Member 1149170820-Mar-15 4:51 
QuestionThe compiler failed with error code -1073741502 Pin
shauncollege23-Sep-13 3:50
shauncollege23-Sep-13 3:50 
GeneralMy vote of 5 Pin
Jaikrishan6-Aug-13 20:38
Jaikrishan6-Aug-13 20:38 
Questionfailed to allocate a managed memory buffer of bytes. the amount of available memory may be low Pin
v.narasa10-Jul-13 23:29
v.narasa10-Jul-13 23:29 
QuestionCannot work alongside data contracts Pin
Rakesh Vasu3-Jul-13 9:41
Rakesh Vasu3-Jul-13 9:41 
QuestionCannot run the example code Pin
giropau27-Jun-13 0:32
giropau27-Jun-13 0:32 
QuestionDoes this work with a Java Client? Pin
niceguyorl12-Jun-13 8:22
niceguyorl12-Jun-13 8:22 
QuestionAborting Pin
Kolompár Lajos27-May-13 21:36
Kolompár Lajos27-May-13 21:36 
QuestionClients under proxy where there is http 1.0 protocol support only Pin
Mosfiqur Rahman2-Mar-13 23:19
Mosfiqur Rahman2-Mar-13 23:19 
GeneralGreat Example Pin
Vasistan Shakkaravarthi26-Feb-13 3:38
Vasistan Shakkaravarthi26-Feb-13 3:38 
QuestionHow do you then call UploadFile from javascript? Pin
dgrassel15-Feb-13 10:56
dgrassel15-Feb-13 10:56 
Preferably using XMLHttpRequest and JSON?
GeneralMy vote of 5 Pin
abellix16-Jan-13 12:18
abellix16-Jan-13 12:18 
QuestionThis option is not supported in the WCF test client because it uses type... Pin
R.SIVAA4-Dec-12 1:54
R.SIVAA4-Dec-12 1:54 
AnswerRe: This option is not supported in the WCF test client because it uses type... Pin
abellix16-Jan-13 12:17
abellix16-Jan-13 12:17 
GeneralMy vote of 5 Pin
DaoNhan15-Oct-12 15:54
DaoNhan15-Oct-12 15:54 
QuestionGetting a "(415) Unsupported Media Type.HTTP GET" error Pin
winny8713-Oct-12 3:37
winny8713-Oct-12 3:37 
AnswerRe: Getting a "(415) Unsupported Media Type.HTTP GET" error Pin
v.narasa9-Jul-13 4:30
v.narasa9-Jul-13 4:30 
AnswerRe: Getting a "(415) Unsupported Media Type.HTTP GET" error Pin
v.narasa10-Jul-13 23:23
v.narasa10-Jul-13 23:23 
QuestionAccess is denied question Pin
Member 836848113-Sep-12 3:49
Member 836848113-Sep-12 3:49 
AnswerRe: Access is denied question Pin
chirag zadafia13-Sep-12 20:50
chirag zadafia13-Sep-12 20:50 
QuestionUnable to cast object of type 'System.String' to type 'System.IO.Stream'. Pin
chirag zadafia6-Sep-12 3:19
chirag zadafia6-Sep-12 3:19 
QuestionHi Pin
apandey214-Sep-12 3:10
apandey214-Sep-12 3:10 
GeneralMy vote of 5 Pin
seankearon25-Jul-12 21:08
seankearon25-Jul-12 21:08 
Questionwhen the client close the connection with WCF service? Pin
informes empresas27-Jun-12 2:00
informes empresas27-Jun-12 2:00 
QuestionThank you Pin
Rajalingam2219-Jun-12 19:18
Rajalingam2219-Jun-12 19:18 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.