简体   繁体   中英

Post multipart/form-data file from stream with HttpClient

The problem: Is it possible using httpclient to post a HttpPostedFileBase(only available in memory, not on disk.) to another endpoint?

What I've tried:

I have the following controller, we are posting in a file from our frontend and it binds to the file parameter.

public class HomeController : Controller
{
    [System.Web.Http.HttpPost]
    public async Task<string> Index(HttpPostedFileBase file)
    {
        //file is not null here, everything works as it should.

        //Here im preparing a multipart/form-data request to my api endpoint
        var fileStreamContent = new StreamContent(file.InputStream);
        using (var client = new HttpClient())
        using (var formData = new MultipartFormDataContent())
        {
            formData.Add(fileStreamContent);
            var response = await client.PostAsync("http://restapi.dev/api/files/add", formData);
            var result = await response.Content.ReadAsStringAsync();
            return result;
        }
    }
}

I need to pass this request on to another application that's not publicly available(so we can't post directly from the client) That controller looks like this:

[RoutePrefix("api/files")]
public class FilesController : ApiController
{
    [HttpPost]
    [Route("add")]
    public async Task<HttpResponseMessage> Add(HttpPostedFileBase file)
    {
        //This is the problem, file is always null when I post from my backend.
        var files = HttpContext.Current.Request.Files.Count > 0 ? HttpContext.Current.Request.Files[0] : null;
        return Request.CreateResponse(HttpStatusCode.OK);
    }
}

file is always null, so are files . What am I missing? When I use postman and post directly to the API endpoint, it works. So im guessing that im doing something wrong in my HomeController ?

You can improve on this with newer tech.

In webapi, you can write a controller method like this:

//[Route("api/Foo")] //maybe?
[HttpPost]
public async Task<HttpResponseMessage> MyResourceProxy(HttpRequestMessage request)

now you can take that request and rewrite its RequestUri property:

request.RequestUri = new Uri(blah);

new up an HttpClient and forward the request:

HttpClient client = new HttpClient();
//make sure that this fails if it's hung for more than 30 seconds
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
try
{
    response = await client.SendAsync(request,
                                      HttpCompletionOption.ResponseHeadersRead, 
                                      cts.Token);
}
catch (TaskCanceledException)
{
    response = new HttpResponseMessage(HttpStatusCode.GatewayTimeout);
}

make sure that everything gets disposed:

request.RegisterForDispose(new IDisposable[] {request, client, cts, response});

and then return the response

return response;

I added a timeout mechanism in that might not be appropriate for you needs.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM