You program a client that needs to upload a file to the server. What are the options? You should probably look into
System.Net namespace where you can either program at
WebRequest/WebResponse level or make use of one of the convenient methods on the
The 1st approach is described here and
WebClient provides a bunch methods with self-explaining names:
UploadData, UploadFile, UploadString and
UploadValues. There are also asynchronous versions of the methods as well as overloads to send data over FTP.
However, .NET doesn’t provide an out-of-the-box solution when you need to upload a file together with accompanying metadata. The metadata can be anything and is usually presented as name/value pairs, that is exactly what
WebClient.UploadValues is supposed to do. And it does, but it doesn’t allow to send some binary array (a file) along with metadata.
UploadValues can send a string-string dictionary of values. We can theoretically Base64-encode the file but that’s probably not what the other side expects. Content type is set to
application/x-www-form-urlencoded. What’s that mean? This code:
NameValueCollection data = new NameValueCollection(); data.Add("id", "10"); data.Add("param", "content"); data.Add("file", "A0BA"); WebClient client = new WebClient(); client.UploadValues("http://dzimchuk-mbl:14393/", null, data);
is going to produce the following request:
Not really what we’re looking for. We want to send our stuff as
multipart/form-data so that we could send dictionary values as well as binary content of a file in one request as just separate parts.
It’s interesting to note that
WebClient.UploadFile does set content type to
- it doesn'r properly format the request and
- there is no way to piggyback any additional metadata:
Let’s try to send a text file called “hello.txt” containing a single string “hello”:
WebClient client = new WebClient(); client.UploadFile("http://dzimchuk-mbl:14393/", null, @"d:\hello.txt");
and here’s the request:
Yep, the request is not properly formatted. You’re going to get away with it with IIS and .NET stack but it’s likely to fail on other servers like Tomcat, for example.
The solution to both of these problems is found in this article and there is a handy file uploader class written by Gregory Prentice.
He explains why
WebClient incorrectly constructs the request:
There are basically three boundaries that need to be define: Begining or definition, Content, and Ending. The Begining boundary basically consists of a bytes array that defines to the receiving end what values to expect as a separator of the multipart data. The Content boundary consists of the "--" string appended to the begining of the boundary and is used to separate the actual multipart data during the upload. The Ending boundary consists of the "--" appended to the end of the Content boundary. As an example the following represents each of these as present in the source code:
BeginBoundary = "ou812--------------8c405ee4e38917c";
ContentBoundary = "--" + BeginBoundary;
EndingBoundary = ContentBoundary + "--";
Using the proposed uploader class called
MultipartForm is as easy as:
MultipartForm form = new MultipartForm("http://dzimchuk-mbl:14393/"); form.SetField("id", "10"); form.SetField("param", "content"); form.SendFile(@"d:\hello.txt");
See we upload a text file together with additional dictionary of parameters. And here’s the request:
By default it sets the content type of the uploaded file to
text/xml but if you need to upload a binary file you can specify ‘binary’ for the
You can find a slightly brushed up version of
MultipartForm class below.