Uploading multiple files and form fields in .Net
Recently while building a small application for a friend of mine I came across a part where I needed something that I just couldn’t find in the .Net framework. I needed to upload two files (and specify their fieldnames) and a few form fields to a webpage. I thought this would be easy just using the provided WebClient class but after looking closer at it I found that it didn’t really offer anything of what I needed. Trying not to feel let down by this I started to write my own extended version of WebClient with a few new tricks that the old one couldn’t do.
I tried to keep it as simple as possible so instead of writing new classes to hold form and file data I used two Dictionary<string, string> to pass the values. The Key is the fieldname and the Value the value or the path to the file. An example of uploading a few files and adding a few fields to that could look like this:
Dictionary<string, string> FormFiles = new Dictionary<string, string>();
FormFiles.Add("mypet", "C:\\cat.jpg");
FormFiles.Add("me", "C:\\myself.jpg");
Dictionary<string, string> FormFields = new Dictionary<string, string>();
FormFields.Add("age", "22");
FormFields.Add("location", "Sweden");
WebClientEx WC = new WebClientEx();
try
{
WC.UploadData("http://www.myserver.com/upload.aspx", FormFiles, FormFields);
}
catch(WebException E)
{
System.Console.WriteLine("Upload failed with HTTP-Code" + ((HttpWebResponse)E.Response).StatusCode.ToString());
System.Console.ReadKey();
return;
}
It doesn’t look that hard does it? It’s perfect to use if you need to “submit” a form on a webpage from your c# application or just need to send several files to a webserver in the same request. Below is the complete code for WebClientEx displayed. You may use it for anything you want as long as you don’t claim it as your own. If you use it in a project of yours then please write a comment or send me an email about it!
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using CrazyBeavers.Utils;
namespace CrazyBeavers.Net
{
/// <summary>
/// Provides common methods for sending data to and receiving data from a resource identified by a URI. This version comes with extended support for uploading files and formfields.
/// </summary>
public class WebClientEx : WebClient
{
public byte[] UploadData(string Address, Dictionary<string, string> Files, Dictionary<string, string> Fields)
{
string Boundary = "----------" + DateTime.Now.Ticks.ToString("x");
byte[] BoundaryEnd = Encoding.ASCII.GetBytes("\r\n--" + Boundary + "--");
MemoryStream MS = new MemoryStream();
byte[] Buffer;
foreach (KeyValuePair<string, string> Field in Fields)
{
StringBuilder Header = new StringBuilder();
Header.Append("\r\n");
Header.Append("--");
Header.Append(Boundary);
Header.Append("\r\n");
Header.Append("Content-Disposition: form-data; name=\"");
Header.Append(Field.Key);
Header.Append("\"");
Header.Append("\r\n");
Header.Append("\r\n");
Header.Append(Field.Value);
byte[] PostHeaderBytes = Encoding.UTF8.GetBytes(Header.ToString());
MS.Write(PostHeaderBytes, 0, PostHeaderBytes.Length);
}
foreach (KeyValuePair<string, string> File in Files)
{
StringBuilder Header = new StringBuilder();
Header.Append("\r\n");
Header.Append("--");
Header.Append(Boundary);
Header.Append("\r\n");
Header.Append("Content-Disposition: form-data; name=\"");
Header.Append(File.Key);
Header.Append("\"; filename=\"");
Header.Append(Path.GetFileName(File.Value));
Header.Append("\"");
Header.Append("\r\n");
Header.Append("Content-Type: ");
Header.Append(MimeHelper.GetMimeTypeForExtension(Path.GetExtension(File.Value)));
Header.Append("\r\n");
Header.Append("\r\n");
byte[] PostHeaderBytes = Encoding.UTF8.GetBytes(Header.ToString());
FileStream FS = new FileStream(File.Value, FileMode.Open, FileAccess.Read);
MS.Write(PostHeaderBytes, 0, PostHeaderBytes.Length);
Buffer = new Byte[checked((UInt32)Math.Min(4096, (Int32)FS.Length))];
Int32 BytesRead = 0;
while ((BytesRead = FS.Read(Buffer, 0, Buffer.Length)) != 0)
MS.Write(Buffer, 0, BytesRead);
FS.Close();
}
MS.Write(BoundaryEnd, 0, BoundaryEnd.Length);
MS.Position = 0;
if (this.Headers["Content-Type"] != null)
this.Headers.Remove("Content-Type");
this.Headers.Add("Content-Type", "multipart/form-data; boundary=" + Boundary);
Buffer = new byte[MS.Length];
MS.Read(Buffer, 0, (Int32)MS.Length);
return this.UploadData(Address, "POST", Buffer);
}
}
}
I won’t go in explaining line of this code as I’ve done in a few other samples. The code isn’t that hard to understand if you have moved beyond the “beginner stage” of coding. The thing worth mentioning is the part with MimeHelper.GetMimeTypeForExtension() which is a small function I wrote which checks the registry for any available mimetype for a given extension. I’ve included that code below aswell since it’s needed for WebClientEx.
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Microsoft.Win32;
namespace CrazyBeavers.Utils
{
public static class MimeHelper
{
public static string GetMimeTypeForExtension(string FileExt)
{
if (!FileExt.StartsWith("."))
FileExt = "." + FileExt;
string Mime = "application/octet-stream";
RegistryKey Classes = Registry.ClassesRoot;
RegistryKey FileKey = Classes.OpenSubKey(FileExt);
if (FileKey != null)
{
Mime = (string)FileKey.GetValue("Content Type", "application/octet-stream");
FileKey.Close();
}
Classes.Close();
return Mime;
}
}
}
Enjoy!