WCF Streaming: Upload/Download Files Over HTTP
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:
- wsHttpBinding
- 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.
[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;
}
}
}
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.
<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.
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:
<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 the system.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.
发表评论
7cJPa7 Wow! I cant think I have found your blog. Extremely helpful information.
N4omOh I think this is a real great article post.Thanks Again. Great.
The best solution is to know the secret of lustrous thick hair.
ZijZEa Looking forward to reading more. Great blog post.Really looking forward to read more. Want more.
VszpYz Thanks again for the blog article.Really thank you! Much obliged.
lH3oXp There is also one other method to increase traffic for your web site that is link exchange, therefore you also try it
Eyv0op Your style is so unique compared to other people I have read stuff from. Many thanks for posting when you have the opportunity, Guess I all just bookmark this site.
er1pLi Thanks-a-mundo for the article post.Thanks Again. Great.
U29QB3 Very good blog post. I absolutely love this site. Thanks!
ZVFgDF wow, awesome post.Thanks Again. Will read on
fu1CFp Thanks for sharing this fine article. Very interesting ideas! (as always, btw)
drmfxU Saved as a favorite, I like your web site!
qaHWQF Spot on with this write-up, I absolutely feel this site needs a lot more attention. I all probably be returning to read more, thanks for the advice!
Tz5mhF Your style is really unique compared to other folks I have read stuff from. Thank you for posting when you ave got the opportunity, Guess I will just book mark this web site.
plMog4 It'аs really a cool and useful piece of information. I'аm happy that you shared this helpful information with us. Please stay us up to date like this. Thanks for sharing.
nvd3nb Im obliged for the post.Thanks Again. Will read on
Very nice blog post. I certainly appreciate this site. Thanks!
Wn6XRE I think other web-site proprietors should take this website as an model, very clean and great user friendly style and design, as well as the content. You are an expert in this topic!
roKB0g Wow, that as what I was searching for, what a material! present here at this weblog, thanks admin of this web page.
Ln0FPd Very nice info and right to the point. I am not sure if this is truly the best place to ask but do you guys have any ideea where to hire some professional writers? Thank you
c9PMi8 Wow, amazing weblog format! How lengthy have you been blogging for? you make running a blog look easy. The whole look of your web site is fantastic, let alone the content material!
IVOBZr
tGccTx Im grateful for the article.Really looking forward to read more. Want more.
LHRoHV This is a terrific website. and i need to take a look at this just about every day of your week ,
KeEZ6Q It as laborious to search out knowledgeable people on this matter, but you sound like you understand what you are speaking about! Thanks
ra0z6P very couple of internet sites that transpire to become detailed below, from our point of view are undoubtedly nicely worth checking out
Xn5qRS Thanks for the blog. Fantastic.
Mxu7Gi Im no pro, but I believe you just crafted an excellent point. You certainly comprehend what youre talking about, and I can truly get behind that. Thanks for being so upfront and so truthful.
lLfWQ9 A round of applause for your blog. Will read on...
aUE2HU Major thanks for the article post.Thanks Again. Much obliged.
5zVYzH Great website. Lots of useful information here. I'm sending it to several buddies ans additionally sharing in delicious. And of course, thanks for your effort!
uA1ct4 I was suggested this web site by my cousin. I'm not sure whether this post is written by him as no one else know such detailed about my trouble. You are incredible! Thanks!
dt3qyh I truly appreciate this post. I have been looking all over for this! Thank goodness I found it on Bing. You have made my day! Thank you again
w3GMP6 Excellent blog here! Also your website loads up fast! What host are you using? Can I get your affiliate link to your host? I wish my site loaded up as quickly as yours lol
91VMCT Hey there! I've been reading your weblog for a while now and finally got the bravery to go ahead and give you a shout out from Humble Texas! Just wanted to tell you keep up the great work!
ikkHsK Looking forward to reading more. Great post.Thanks Again. Great.
6tKZMT A big thank you for your post.Thanks Again. Cool.
KWEvsn Say, you got a nice blog article.Really looking forward to read more. Will read on...
nXzhsn Awesome blog.Really looking forward to read more. Will read on...
6HpYC0 Thanks a lot for the blog post. Will read on...
VxBmU4 Im obliged for the blog article.Much thanks again. Really Great.
3zCluy Muchos Gracias for your blog.Much thanks again. Want more.
Glsqng Thanks for sharing, this is a fantastic blog.Much thanks again.
NpWkdw Wow, great post.Much thanks again. Keep writing.
I really enjoy the blog. Much obliged.
Thanks a lot for the post.Really thank you! Much obliged.
4K4V0O Very informative article post. Really Great.
DAULy3 I am so grateful for your blog post.Much thanks again. Fantastic.
BER6m7 This is one awesome article post.Really thank you! Much obliged.
eOQPkG Great blog.Thanks Again. Really Cool.