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.
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;
}
}
}
}
}
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;
}
}
0 Comments