The generic way to handle this sort of with HttpClient is to use a HttpMessageHandler as you might other work to do e.g. adding authorization headers, signing the message etc.
I've also re-written this using Task based syntax as it's more ideomatic HttpClient - the caller can call .Result as needed.
private await Task<System.IO.Stream> Upload(string url, string param1, Stream fileStream, byte[] fileBytes)
{
HttpContent stringContent = new StringContent(param1);
HttpContent fileStreamContent = new StreamContent(fileStream);
HttpContent bytesContent = new ByteArrayContent(fileBytes);
var handler = new HttpClientHandler();
var md5Handler = new RequestContentMd5Handler();
md5Handler.InnerHandler = handler;
using (HttpClient client = new HttpClient(md5Handler))
{
using (MultipartFormDataContent formData = new MultipartFormDataContent())
{
formData.Add(stringContent, "param1", "param1");
formData.Add(fileStreamContent, "file1", "file1");
formData.Add(bytesContent, "file2", "file2");
using (var response = await client.PostAsync(url, formData))
{
if (!response.IsSuccessStatusCode)
{
return null;
}
return await response.Content.ReadAsStreamAsync();
}
}
}
}
Also, it's generally poor practice to re-create the HttpClient on each request (see What is the overhead of creating a new HttpClient per call in a WebAPI client? etc), but I've left that here in keeping with the style of the question.
Here's the handler used...
/// <summary>
/// Handler to assign the MD5 hash value if content is present
/// </summary>
public class RequestContentMd5Handler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.Content == null)
{
return await base.SendAsync(request, cancellationToken);
}
await request.Content.AssignMd5Hash();
var response = await base.SendAsync(request, cancellationToken);
return response;
}
}
And the extensions methods...
/// <summary>
/// Compute and assign the MD5 hash of the content.
/// </summary>
/// <param name="httpContent"></param>
/// <returns></returns>
public static async Task AssignMd5Hash(this HttpContent httpContent)
{
var hash = await httpContent.ComputeMd5Hash();
httpContent.Headers.ContentMD5 = hash;
}
/// <summary>
/// Compute the MD5 hash of the content.
/// </summary>
/// <param name="httpContent"></param>
/// <returns></returns>
public static async Task<byte[]> ComputeMd5Hash(this HttpContent httpContent)
{
using (var md5 = MD5.Create())
{
var content = await httpContent.ReadAsStreamAsync();
var hash = md5.ComputeHash(content);
return hash;
}
}
Makes it easy to unit test the various parts.