Multipart Form File Uploads Using WebClient

If you need to upload a file to an API using a multipart form then you’re generally better off using HttpClient rather than WebClient, unfortunatly however HttpClient isn’t available in SSDT so if you need to upload a file from a script task then you’re stuck with WebClient.

The below code is based on a StackOverflow answer here.

MultiPartFormUpload.cs

First off I added the below class to my script task.

	
public class MultiPartFormUpload
{
	public class MimePart
	{
		NameValueCollection _headers = new NameValueCollection();
		byte[] _header;

		public NameValueCollection Headers
		{
			get { return _headers; }
		}

		public byte[] Header
		{
			get { return _header; }
		}

		public long GenerateHeaderFooterData(string boundary)
		{
			StringBuilder stringBuilder = new StringBuilder();

			stringBuilder.Append("--");
			stringBuilder.Append(boundary);
			stringBuilder.AppendLine();
			foreach (string key in _headers.AllKeys)
			{
				stringBuilder.Append(key);
				stringBuilder.Append(": ");
				stringBuilder.AppendLine(_headers[key]);
			}
			stringBuilder.AppendLine();

			_header = Encoding.UTF8.GetBytes(stringBuilder.ToString());

			return _header.Length + Data.Length + 2;
		}

		public Stream Data { get; set; }
	}

	public class UploadResponse
	{
		public UploadResponse(HttpStatusCode httpStatusCode, string responseBody)
		{
			HttpStatusCode = httpStatusCode;
			ResponseBody = responseBody;
		}

		public HttpStatusCode HttpStatusCode { get; set; }

		public string ResponseBody { get; set; }
	}

	public UploadResponse Upload(string url, NameValueCollection requestHeaders, NameValueCollection requestParameters, List<FileInfo> files)
	{
		using (WebClient client = new WebClient())
		{
			List<MimePart> mimeParts = new List<MimePart>();

			try
			{
				foreach (string key in requestHeaders.AllKeys)
				{
					client.Headers.Add(key, requestHeaders[key]);
				}

				foreach (string key in requestParameters.AllKeys)
				{
					MimePart part = new MimePart();

					part.Headers["Content-Disposition"] = "form-data; name=\"" + key + "\"";
					part.Data = new MemoryStream(Encoding.UTF8.GetBytes(requestParameters[key]));

					mimeParts.Add(part);
				}

				foreach (FileInfo file in files)
				{
					MimePart part = new MimePart();
					string name = file.Extension.Substring(1);
					string fileName = file.Name;

					part.Headers["Content-Disposition"] = "form-data; name=\"" + name + "\"; filename=\"" + fileName + "\"";
					part.Headers["Content-Type"] = "application/octet-stream";

					part.Data = new MemoryStream(File.ReadAllBytes(file.FullName));

					mimeParts.Add(part);
				}

				string boundary = "----------" + DateTime.Now.Ticks.ToString("x");
				client.Headers.Add(HttpRequestHeader.ContentType, "multipart/form-data; boundary=" + boundary);

				long contentLength = 0;

				byte[] _footer = Encoding.UTF8.GetBytes("--" + boundary + "--\r\n");

				foreach (MimePart mimePart in mimeParts)
				{
					contentLength += mimePart.GenerateHeaderFooterData(boundary);
				}

				byte[] buffer = new byte[8192];
				byte[] afterFile = Encoding.UTF8.GetBytes("\r\n");
				int read;

				using (MemoryStream memoryStream = new MemoryStream())
				{
					foreach (MimePart mimePart in mimeParts)
					{
						memoryStream.Write(mimePart.Header, 0, mimePart.Header.Length);

						while ((read = mimePart.Data.Read(buffer, 0, buffer.Length)) > 0)
							memoryStream.Write(buffer, 0, read);

						mimePart.Data.Dispose();

						memoryStream.Write(afterFile, 0, afterFile.Length);
					}

					memoryStream.Write(_footer, 0, _footer.Length);
					byte[] responseBytes = client.UploadData(url, memoryStream.ToArray());
					string responseString = Encoding.UTF8.GetString(responseBytes);
					return new UploadResponse(HttpStatusCode.OK, responseString);
				}
			}
			catch (Exception ex)
			{
				foreach (MimePart part in mimeParts)
					if (part.Data != null)
						part.Data.Dispose();

				if (ex.GetType().Name == "WebException")
				{
					WebException webException = (WebException)ex;
					HttpWebResponse response = (HttpWebResponse)webException.Response;
					string responseString;

					using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
					{
						responseString = reader.ReadToEnd();
					}

					return new UploadResponse(response.StatusCode, responseString);
				}
				else
				{
					throw;
				}
			}
		}
	}
}
	
ScriptMain.cs

This class can then be used in your standard ScriptMain class like so.

	
public void Main()
{
	string filePath = Dts.Variables["User::output_file"].Value.ToString();
	string apiId = Dts.Variables["User::apiId"].Value.ToString();
	string apiToken = Dts.Variables["User::apiToken"].Value.ToString();

	MultiPartFormUpload multiPartFormUpload = new MultiPartFormUpload();

	NameValueCollection headers = new NameValueCollection();
	headers.Add("auth_id", apiId);
	headers.Add("auth_token", apiToken);

	List<FileInfo> files = new List<FileInfo>() { new FileInfo(filePath) };
	
	try
	{
		MultiPartFormUpload.UploadResponse response = multiPartFormUpload.Upload("https://api.multipart-form-upload", headers, new NameValueCollection() { }, files);

		Dts.TaskResult = (int)ScriptResults.Success;
	}
	catch (Exception ex)
	{
		Dts.TaskResult = (int)ScriptResults.Failure;
	}
}
	

Leave a Reply

Your email address will not be published. Required fields are marked *