从 HttpClient 向 webapi 提交 File 和 Json 数据

Posted

技术标签:

【中文标题】从 HttpClient 向 webapi 提交 File 和 Json 数据【英文标题】:Submitting File and Json data to webapi from HttpClient 【发布时间】:2015-06-26 18:32:37 【问题描述】:

我想将文件和 json 数据从 HttpClient 发送到 web api 服务器。 我似乎无法通过有效负载访问服务器中的 json,只能作为 json var。

 public class RegulationFilesController : BaseApiController
    
        public void PostFile(RegulationFileDto dto)
        
            //the dto is null here
        
    

这里是客户端:

   using (var client = new HttpClient())
                
                    using (var content = new MultipartFormDataContent())
                    
                        client.BaseAddress = new Uri(ConfigurationManager.AppSettings["ApiHost"]);
                        content.Add(new StreamContent(File.OpenRead(@"C:\\Chair.png")), "Chair", "Chair.png");
                        var parameters = new RegulationFileDto
                        
                            ExternalAccountId = "1234",
                        ;
                        javascriptSerializer serializer = new JavaScriptSerializer();
                        content.Add(new StringContent(serializer.Serialize(parameters), Encoding.UTF8, "application/json"));

                            var resTask = client.PostAsync("api/RegulationFiles", content); //?ApiKey=24Option_key
                            resTask.Wait();
                            resTask.ContinueWith(async responseTask =>
                            
                                var res = await responseTask.Result.Content.ReadAsStringAsync();


                            
                                );

                    
                

这个例子可以工作:HttpClient Multipart Form Post in C# 但只能通过表单数据而不是有效负载。

能否请您建议如何访问文件以及提交的json和文件在同一请求中?

谢谢

【问题讨论】:

【参考方案1】:

我尝试了许多不同的方式来提交文件数据和元数据,这是我发现的最好的方法:

不要使用 MultipartFormDataContent,文件数据只使用 StreamContent。通过这种方式,您可以流式传输文件上传,这样您就不会在服务器上占用太多 RAM。 MultipartFormDataContent 要求您将整个请求加载到内存中,然后将文件保存到某个地方的本地存储中。通过流式传输,您还可以将流复制到其他位置,例如 Azure 存储容器。

这解决了二进制数据的问题,现在解决了元数据的问题。为此,请使用自定义标头并将您的 JSON 序列化为该标头。您的控制器可以读取自定义标头并将其反序列化为您的元数据 dto。 headers有大小限制,见here(8-16KB),数据量很大。如果您需要更多空间,您可以执行两个单独的请求,一个是 POST 最低需求,然后是一个 PATCH 来更新任何需要超过标头可以容纳的属性。

示例代码:

public class RegulationFilesController : BaseApiController

    public async Task<IHttpActionResult> Post()
    
        var isMultipart = this.Request.Content.IsMimeMultipartContent();

        if (isMultipart)
        
            return this.BadRequest("Only binary uploads are accepted.");
        

        var headerDto = this.GetJsonDataHeader<RegulationFileDto>();
        if(headerDto == null)
        
            return this.BadRequest("Missing X-JsonData header.");
        

        using (var stream = await this.Request.Content.ReadAsStreamAsync())
        
            if (stream == null || stream.Length == 0)
            
                return this.BadRequest("Invalid binary data.");
            

            //save stream to disk or copy to another stream

            var model = new RegulationFile(headerDto);

            //save your model to the database

            var dto = new RegulationFileDto(model);

            var uri = new Uri("NEW URI HERE");

            return this.Created(uri, dto);
        
    

    private T GetJsonDataHeader<T>()
    
        IEnumerable<string> headerCollection;

        if (!this.Request.Headers.TryGetValues("X-JsonData", out headerCollection))
        
            return default(T);
        

        var headerItems = headerCollection.ToList();

        if (headerItems.Count() != 1)
        
            return default(T);
        

        var meta = headerItems.FirstOrDefault();

        return !string.IsNullOrWhiteSpace(meta) ? JsonConvert.DeserializeObject<T>(meta) : default(T);
    

【讨论】:

以上是关于从 HttpClient 向 webapi 提交 File 和 Json 数据的主要内容,如果未能解决你的问题,请参考以下文章

从统一 C# POST json 字符串到 webAPI; HttpClient 还是 UnityWebRequest?

在 HttpClient 中解压 Brotli HttpResponse

使用 webapi 中的 HttpClient 使用 xml

HttpClient 不报告从 Web API 返回的异常

.net HttpClient 与自定义 JsonConverter

.NET WebApi HttpClient 未将 Windows 身份验证凭据发送到同一域