Have you ever been in a situation where you needed to upload multiple files to a remote host and pass additional parameters in the request? Unfortunately there’s nothing in the BCL that allows us to achieve this out of the box.
We have the UploadFile method but it is restricted to a single file and doesn’t allow us to pass any additional parameters. So let’s go ahead and write such method. The important part is that this method must comply with RFC 1867 so that the remote web server can successfully parse the information.
First we define a model representing a single file to be uploaded:
public class UploadFile { public UploadFile() { ContentType = "application/octet-stream"; } public string Name { get; set; } public string Filename { get; set; } public string ContentType { get; set; } public Stream Stream { get; set; } }
And here’s a sample UploadFiles method implementation:
public byte[] UploadFiles(string address, IEnumerable<UploadFile> files, NameValueCollection values) { var request = WebRequest.Create(address); request.Method = "POST"; var boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x", NumberFormatInfo.InvariantInfo); request.ContentType = "multipart/form-data; boundary=" + boundary; boundary = "--" + boundary; using (var requestStream = request.GetRequestStream()) { // Write the values foreach (string name in values.Keys) { var buffer = Encoding.ASCII.GetBytes(boundary + Environment.NewLine); requestStream.Write(buffer, 0, buffer.Length); buffer = Encoding.ASCII.GetBytes(string.Format("Content-Disposition: form-data; name=\"{0}\"{1}{1}", name, Environment.NewLine)); requestStream.Write(buffer, 0, buffer.Length); buffer = Encoding.UTF8.GetBytes(values[name] + Environment.NewLine); requestStream.Write(buffer, 0, buffer.Length); } // Write the files foreach (var file in files) { var buffer = Encoding.ASCII.GetBytes(boundary + Environment.NewLine); requestStream.Write(buffer, 0, buffer.Length); buffer = Encoding.UTF8.GetBytes(string.Format("Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"{2}", file.Name, file.Filename, Environment.NewLine)); requestStream.Write(buffer, 0, buffer.Length); buffer = Encoding.ASCII.GetBytes(string.Format("Content-Type: {0}{1}{1}", file.ContentType, Environment.NewLine)); requestStream.Write(buffer, 0, buffer.Length); file.Stream.CopyTo(requestStream); buffer = Encoding.ASCII.GetBytes(Environment.NewLine); requestStream.Write(buffer, 0, buffer.Length); } var boundaryBuffer = Encoding.ASCII.GetBytes(boundary + "--"); requestStream.Write(boundaryBuffer, 0, boundaryBuffer.Length); } using (var response = request.GetResponse()) using (var responseStream = response.GetResponseStream()) using (var stream = new MemoryStream()) { responseStream.CopyTo(stream); return stream.ToArray(); } }
And here’s a sample usage:
using (var stream1 = File.Open("test.txt", FileMode.Open)) using (var stream2 = File.Open("test.xml", FileMode.Open)) using (var stream3 = File.Open("test.pdf", FileMode.Open)) { var files = new[] { new UploadFile { Name = "file", Filename = "test.txt", ContentType = "text/plain", Stream = stream1 }, new UploadFile { Name = "file", Filename = "test.xml", ContentType = "text/xml", Stream = stream2 }, new UploadFile { Name = "file", Filename = "test.pdf", ContentType = "application/pdf", Stream = stream3 } }; var values = new NameValueCollection { { "key1", "value1" }, { "key2", "value2" }, { "key3", "value3" }, }; byte[] result = UploadFiles("http://localhost:1234/upload", files, values); }
In this example we are uploading 3 values and 3 files to the remote host.
Next time I will show how to improve this code by adding an asynchronous version using the TPL library in .NET 4.0.
15 thoughts on “Uploading multiple files with C#”
Very good explanation..Very neatly and clearly explained
I followed your source code but it throws exception “The remote server returned an error: (405) Method Not Allowed.”
Do I have to configure anything else on the IIS server? I’m using IIS 7 and windows 7.
I create a folder as your example “http://localhost:8080/Upload” and map to “C:\inetpub\site1.com\Upload”
I have spent the last two days at work trying to get a concise and relevant explantion of this simple process, can i say thankyou. You have no idea, brilliant, simple works liek a charm
Hi,
Could you create an working example in VB.net by any chance? I’ve tried to convert but without success …
Thanks –
Hi,
I just try your code to upload image data to server like this:
var files = new[]
{
new UploadFile {
Name = “screenshot”,
Filename = “screenshot.jpg”,
ContentType = “image/jpeg”,
Stream = ms
}
};
var parameters = new NameValueCollection
{
{ “username”, uname },
{ “password”, pwd },
{ “type”, “screen” },
{ “id”, screen_id },
};
byte[] bytes = UploadFiles(uploadUrl, files, parameters);
The result is that the parameters can be detected by the server, but the files data cannot be detected and return 500 error. I finally cannot figure out just why this cannot work. Do you have some ideas about this?
please sample php code for get file in your code.
send file with C# to PHP and save in webserver Directory.
send for example:
user (Get with $_POST in php for echo user)
file.avi (Get with $_FILES in php for save file)
and Show log in C#:
return to C# for show Result (File sented or user is Wrong or file send error Retry again(button for retry))
Lifesaver. Much appreciated.
very good explanation & really helpful.
Thank you very much buddy.
Working, Big thanks for good example 🙂
Dear to ask us if you have an example or can guide me to upload files and data to see from a desktop application with a Apacha c # and php servidro
agradecere your support thanks
Thank you so much for this example code.I’m new to c# and tried to write an upload with the httpclient implementation and failed so often. 🙂 Wonderful !!!
Very nice. How will the uri on the receiving end handle the content though? That is separating the bytes into the 3 files and 3 additional parameters. I really need help on that
Very good! It worked perfectly!
Thanks man,
You saved my week! I was struggling with a post and never noticed the extra 2 dashes (–) as an escape sequence before the actual boundary!
You are a hero!
So I cant seem to get this working…. I am getting the following error message on:
byte[] result = UploadFiles(“https://retrobit.games/save.php”, files, values);
The name ‘UploadFiles’ does not exist in the current context
Does anyone have any ideas?
Thanks