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 tried many configuration setting combinations and finally succeeded in transferring large files (I have tested up to 1GB on IE6).
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.
Create WCF Contract with Use Of Interface:-
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:
[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:-
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();
}
}
Changing in
Web.Config File in Wcf (Server Side):-
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.
<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.
<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:
<httpRuntime
maxRequestLength="2097151" //Maxvalue
useFullyQualifiedRedirectUrl="true"
executionTimeout="14400"
/> //can be configured as per
the requirement.
Create Web Site Or
Client or Calling To Services :-
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:
<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:
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
thesystem.web configuration
file section, as below:
<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.
Ref: http://www.codeproject.com