Skip to content

Instantly share code, notes, and snippets.

@ericvoid
Last active August 1, 2023 21:31
Show Gist options
  • Save ericvoid/568d733c90857f010fb860cb5e6aba43 to your computer and use it in GitHub Desktop.
Save ericvoid/568d733c90857f010fb860cb5e6aba43 to your computer and use it in GitHub Desktop.
C# WebClient Multipart Form Upload

I tried to use HttpWebRequest on my Xamarin iOS app to upload files, but there is an issue I was unable to workaround:

System.Net.WebException: Error getting response stream (ReadDoneAsync2): ReceiveFailure
  at System.Net.WebResponseStream.InitReadAsync (System.Threading.CancellationToken cancellationToken)
  at System.Net.WebOperation.Run ()
  at System.Net.WebCompletionSource`1[T].WaitForCompletion ()
  at System.Net.HttpWebRequest.RunWithTimeoutWorker[T]
  ...

Long story short, I found a code snippet on Bitscry blog to send multipart data using WebClient, but I decided to adapt a class I already had (it was used to send multipart with HttpWebRequest). The result is the code below.

https://blog.bitscry.com/2018/08/29/multipart-form-file-uploads-using-webclient/

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace WebClientMultipartExtension
{
public class MultipartFormBuilder
{
static readonly string MultipartContentType = "multipart/form-data; boundary=";
static readonly string FileHeaderTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: application/octet-stream\r\n\r\n";
static readonly string FormDataTemplate = "\r\n--{0}\r\nContent-Disposition: form-data; name=\"{1}\";\r\n\r\n{2}";
public string ContentType { get; private set; }
string Boundary { get; set; }
Dictionary<string, FileInfo> FilesToSend { get; set; } = new Dictionary<string, FileInfo>();
Dictionary<string, string> FieldsToSend { get; set; } = new Dictionary<string, string>();
public MultipartFormBuilder()
{
Boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x");
ContentType = MultipartContentType + Boundary;
}
public void AddField(string key, string value)
{
FieldsToSend.Add(key, value);
}
public void AddFile(FileInfo file)
{
string key = file.Extension.Substring(1);
FilesToSend.Add(key, file);
}
public void AddFile(string key, FileInfo file)
{
FilesToSend.Add(key, file);
}
public MemoryStream GetStream()
{
var memStream = new MemoryStream();
WriteFields(memStream);
WriteStreams(memStream);
WriteTrailer(memStream);
memStream.Seek(0, SeekOrigin.Begin);
return memStream;
}
void WriteFields(Stream stream)
{
if (FieldsToSend.Count == 0)
return;
foreach (var fieldEntry in FieldsToSend)
{
string content = string.Format(FormDataTemplate, Boundary, fieldEntry.Key, fieldEntry.Value);
using (var fieldData = new MemoryStream(Encoding.UTF8.GetBytes(content)))
{
fieldData.CopyTo(stream);
}
}
}
void WriteStreams(Stream stream)
{
if (FilesToSend.Count == 0)
return;
foreach (var fileEntry in FilesToSend)
{
WriteBoundary(stream);
string header = string.Format(FileHeaderTemplate, fileEntry.Key, fileEntry.Value.Name);
byte[] headerbytes = Encoding.UTF8.GetBytes(header);
stream.Write(headerbytes, 0, headerbytes.Length);
using (var fileData = File.OpenRead(fileEntry.Value.FullName))
{
fileData.CopyTo(stream);
}
}
}
void WriteBoundary(Stream stream)
{
byte[] boundarybytes = Encoding.UTF8.GetBytes("\r\n--" + Boundary + "\r\n");
stream.Write(boundarybytes, 0, boundarybytes.Length);
}
void WriteTrailer(Stream stream)
{
byte[] trailer = Encoding.UTF8.GetBytes("\r\n--" + Boundary + "--\r\n");
stream.Write(trailer, 0, trailer.Length);
}
}
}
using (var client = new WebClient())
{
var multipart = new MultipartFormBuilder();
multipart.AddField("POST_field_1", "foobar");
multipart.AddField("local_date", DateTime.Now.ToString("o"));
multipart.AddFile("file", new FileInfo("testfile.jpg"));
client.UploadMultipart(UploadUrl, "POST", multipart);
}
using System;
using System.Net;
using System.Threading.Tasks;
namespace WebClientMultipartExtension
{
public static class WebClientExtensionMethods
{
public static byte[] UploadMultipart(this WebClient client, string address, MultipartFormBuilder multipart)
{
client.Headers.Add(HttpRequestHeader.ContentType, multipart.ContentType);
using (var stream = multipart.GetStream())
{
return client.UploadData(address, stream.ToArray());
}
}
public static byte[] UploadMultipart(this WebClient client, Uri address, MultipartFormBuilder multipart)
{
client.Headers.Add(HttpRequestHeader.ContentType, multipart.ContentType);
using (var stream = multipart.GetStream())
{
return client.UploadData(address, stream.ToArray());
}
}
public static byte[] UploadMultipart(this WebClient client, string address, string method, MultipartFormBuilder multipart)
{
client.Headers.Add(HttpRequestHeader.ContentType, multipart.ContentType);
using (var stream = multipart.GetStream())
{
return client.UploadData(address, method, stream.ToArray());
}
}
public static byte[] UploadMultipart(this WebClient client, Uri address, string method, MultipartFormBuilder multipart)
{
client.Headers.Add(HttpRequestHeader.ContentType, multipart.ContentType);
using (var stream = multipart.GetStream())
{
return client.UploadData(address, method, stream.ToArray());
}
}
public static void UploadMultipartAsync(this WebClient client, Uri address, MultipartFormBuilder multipart)
{
client.Headers.Add(HttpRequestHeader.ContentType, multipart.ContentType);
using (var stream = multipart.GetStream())
{
client.UploadDataAsync(address, stream.ToArray());
}
}
public static void UploadMultipartAsync(this WebClient client, Uri address, string method, MultipartFormBuilder multipart)
{
client.Headers.Add(HttpRequestHeader.ContentType, multipart.ContentType);
using (var stream = multipart.GetStream())
{
client.UploadDataAsync(address, method, stream.ToArray());
}
}
public static void UploadMultipartAsync(this WebClient client, Uri address, string method, MultipartFormBuilder multipart, object userToken)
{
client.Headers.Add(HttpRequestHeader.ContentType, multipart.ContentType);
using (var stream = multipart.GetStream())
{
client.UploadDataAsync(address, method, stream.ToArray(), userToken);
}
}
public static async Task<byte[]> UploadMultipartTaskAsync(this WebClient client, string address, MultipartFormBuilder multipart)
{
client.Headers.Add(HttpRequestHeader.ContentType, multipart.ContentType);
using (var stream = multipart.GetStream())
{
return await client.UploadDataTaskAsync(address, stream.ToArray());
}
}
public static async Task<byte[]> UploadMultipartTaskAsync(this WebClient client, Uri address, MultipartFormBuilder multipart)
{
client.Headers.Add(HttpRequestHeader.ContentType, multipart.ContentType);
using (var stream = multipart.GetStream())
{
return await client.UploadDataTaskAsync(address, stream.ToArray());
}
}
public static async Task<byte[]> UploadMultipartTaskAsync(this WebClient client, string address, string method, MultipartFormBuilder multipart)
{
client.Headers.Add(HttpRequestHeader.ContentType, multipart.ContentType);
using (var stream = multipart.GetStream())
{
return await client.UploadDataTaskAsync(address, method, stream.ToArray());
}
}
public static async Task<byte[]> UploadMultipartTaskAsync(this WebClient client, Uri address, string method, MultipartFormBuilder multipart)
{
client.Headers.Add(HttpRequestHeader.ContentType, multipart.ContentType);
using (var stream = multipart.GetStream())
{
return await client.UploadDataTaskAsync(address, method, stream.ToArray());
}
}
}
}
@alperkizilgilelt
Copy link

I've tried your code but I have gotten bad request (400) error :(
My codes:
using (var client = new WebClient())
{
var multipart = new MultipartFormBuilder();
multipart.AddField("path", args.rootRepo);
multipart.AddFile("file", new FileInfo(args.filepath));

            try
            {
                var response = client.UploadMultipart(url, "POST", multipart);
                return new rDirekArizaResimGonder()
                {
                    success = true,
                    documentId = Encoding.UTF8.GetString(response),
                    fileName = args.filename
                };
            }
            catch (Exception ex)
            {
                return new rDirekArizaResimGonder() { success = false };
            }
        }

@ericvoid
Copy link
Author

ericvoid commented Nov 2, 2020

@alperkizilgilelt what about the server? Does it recognize the URL? Does it give any errors regarding the multipart format?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment