2

Here's a .NET 4.6.2 API web client uploading a file - simple stuff:

var webClient = new ApiWebClient();
webClient.Timeout = timeout;

webClient.Headers.Add( HttpHeaderNames.CONTENT_TYPE, contentType );
webClient.Headers.Add( HttpHeaderNames.CONTENT_DISPOSITION, disp );

foreach (var h in _headers)
     webClient.Headers.Add(h.Key, h.Value);

respBytes = webClient.UploadFile(FullUrl, fileFullPath);

My problem is trying to upload a file in .NET 8 with HttpClient.

Unfortunately I don't have access to the server receiving the request, but it sends back a response that looks like no file was attached. The current iteration of my code is something like this:

var httpClient = GetHttpClient(); // gets from the HttpClientFactory
httpClient.Timeout = new TimeSpan( 0, 0, timeout );

var uriBuilder = BuildUriWithGets();
var req = new HttpRequestMessage( HttpVerb, uriBuilder.ToString() );
req.Headers.TryAddWithoutValidation( HttpHeaderNames.CONTENT_TYPE, contentType );
req.Headers.TryAddWithoutValidation( HttpHeaderNames.CONTENT_DISPOSITION, disp );

foreach (var h in _headers)
    request.Headers.TryAddWithoutValidation(h.Key, h.Value);

HttpResponseMessage resp;

using var requestContent = new MultipartFormDataContent();
using var fileStream = File.OpenRead(fileFullPath);

requestContent.Add(new StreamContent(fileStream));
req.Content = requestContent;

resp = httpClient.Send(req);

Is this the proper way to send a file with headers and query params? I want the file to be the "file" parameter (I've tried iterations on this to no avail).

Moreover, is there a good way to see the raw message being sent via some text dump (I'm running this via Visual Studio). I could compare the ApiWebClient request and the HttpClient request to see what's missing.

3 Answers 3

0

Move Content-Disposition and Content-Type to the file content.

fileContent.Headers.ContentType = new MediaTypeHeaderValue(contentType);
fileContent.Headers.ContentDisposition = new 
ContentDispositionHeaderValue("form-data")
{
  Name = "\"file\"",
  FileName = $"\"{Path.GetFileName(fileFullPath)}\""
};
Sign up to request clarification or add additional context in comments.

1 Comment

Unfortunately this also does not work for me.
0

I have finally made this work. Here's the final code:

            var httpClient = GetHttpClient(); // gets from the HttpClientFactory
            httpClient.Timeout = new TimeSpan( 0, 0, timeout );

            var uriBuilder = BuildUriWithGets();
            var req = new HttpRequestMessage( HttpVerb, uriBuilder.ToString() );

            SetHeaders( req );

            var content = new MultipartFormDataContent();
            var fileContent = new StreamContent( File.OpenRead( fileFullPath ) );
            fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse( contentType );
            content.Add( fileContent, "file", Path.GetFileName( fileFullPath ) );
            req.Content = content;

            using var resp = httpClient.Send( req );

2 Comments

Thank you for contributing to the Stack Overflow community. This may be a correct answer, but it’d be really useful to provide additional explanation of your code so developers can understand your reasoning. This is especially useful for new developers who aren’t as familiar with the syntax or struggling to understand the concepts. Would you kindly edit your answer to include additional details for the benefit of the community?
@JeremyCaney I'm not really sure I can add any additional details. This answer was 100% trial-and-error after many iterations. (There was also a server-side error that someone created that caused all my issues in the previous comment thread.) My only answer here is "I finally settled on doing it this way because it works after trying many methods that did not work." If I could add something more helpful, I would...
-1

WebClient.UploadFile just uploads a raw stream, it doesn't embed it in a Multipart. For that you just need a StreamContent.

Also:

  • You are missing usings.
  • Use await SendAsync not Send where possible.
  • ContentType and ContentDisposition headers go in the content not the request object.
var httpClient = GetHttpClient(); // gets from the HttpClientFactory
httpClient.Timeout = new TimeSpan( 0, 0, timeout );

var uriBuilder = BuildUriWithGets();
using var req = new HttpRequestMessage( HttpVerb, uriBuilder.ToString() );

foreach (var h in _headers)
    req.Headers.TryAddWithoutValidation(h.Key, h.Value);

using var fileStream = File.OpenRead(fileFullPath);
using var requestContent = new StreamContent(fileStream);
requestContent.Headers.ContentType = new MediaTypeHeaderValue(contentType);
requestContent.Headers.ContentDisposition = new ContentDispositionHeaderValue(disp);
req.Content = requestContent;

using var resp = await httpClient.SendAsync(req);
// do stuff with response

To view the raw requests in both cases for debugging purposes, use a transparent proxy such Fiddler.

7 Comments

In your example, you have content.Headers.... what is content? It's never declared in your example. And why do I need headers anywhere other than on the request? Quite confusing. What headers is the server processing? Just the request, right?
Sorry copy paste error, fixed now. Different headers go in different places in this object model, some go on the request and some go in the content, this is because if you for eample create a MultiPart then some of those headers would be repeated separately for each content part. All headers get passed to the server.
My first attempt with your code fails similarly. I guess I need to get insight on the server, why it fails. I used your code like this: requestContent.Headers.ContentType = new MediaTypeHeaderValue( contentType ); requestContent.Headers.ContentDisposition = new ContentDispositionHeaderValue( "form-data" ) { FileName = Path.GetFileName( fileFullPath ), Name = "file", }; req.Content = requestContent;
Server says "missing file or wrong field name". So somehow it doesn't know where "file" is.
Compare working and non working requests in Fiddler, you probably have a missing or incorrect header.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.