从 HttpClient 向 webapi 提交文件和 Json 数据
Submitting File and Json data to webapi from HttpClient
我想将文件和 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();
}
);
}
}
这个例子可以工作:
但只能通过表单数据而不是有效负载。
能否请您建议如何访问文件和提交的 json 以及同一请求下的文件?
谢谢
我尝试了很多不同的方法来提交文件数据和元数据,这是我发现的最好的方法:
不要使用 MultipartFormDataContent,仅将 StreamContent 用于文件数据。这样你就可以流式传输文件上传,这样你就不会在服务器上占用太多内存。 MultipartFormDataContent 要求您将整个请求加载到内存中,然后将文件保存到某个地方的本地存储中。通过流式传输,您还可以将流复制到其他位置,例如 Azure 存储容器。
这解决了二进制数据的问题,现在是元数据的问题。为此,请使用自定义 header 并将您的 JSON 序列化到其中。您的控制器可以读取自定义 header 并将其反序列化为您的元数据 dto。大小限制为headers,见here(8-16KB),属于大数据量。如果您需要更多 space,您可以执行两个单独的请求,一个是 POST 最低需求,然后是一个 PATCH 来更新任何需要超过 header 的属性。
示例代码:
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);
}
}
我想将文件和 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();
}
);
}
}
这个例子可以工作: 但只能通过表单数据而不是有效负载。
能否请您建议如何访问文件和提交的 json 以及同一请求下的文件?
谢谢
我尝试了很多不同的方法来提交文件数据和元数据,这是我发现的最好的方法:
不要使用 MultipartFormDataContent,仅将 StreamContent 用于文件数据。这样你就可以流式传输文件上传,这样你就不会在服务器上占用太多内存。 MultipartFormDataContent 要求您将整个请求加载到内存中,然后将文件保存到某个地方的本地存储中。通过流式传输,您还可以将流复制到其他位置,例如 Azure 存储容器。
这解决了二进制数据的问题,现在是元数据的问题。为此,请使用自定义 header 并将您的 JSON 序列化到其中。您的控制器可以读取自定义 header 并将其反序列化为您的元数据 dto。大小限制为headers,见here(8-16KB),属于大数据量。如果您需要更多 space,您可以执行两个单独的请求,一个是 POST 最低需求,然后是一个 PATCH 来更新任何需要超过 header 的属性。
示例代码:
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);
}
}