尝试接受具有大文件的模型时出现 .net API 错误
Posted
技术标签:
【中文标题】尝试接受具有大文件的模型时出现 .net API 错误【英文标题】:.net API error when attempting to accept model with large file 【发布时间】:2021-08-27 12:56:19 【问题描述】:我有一个 API 方法,当一个大文件传递给它时,它接收一个空模型参数。 我创建了一个测试客户端来测试这个端点。测试客户端和 API 都具有相同的模型,并且都使用 .NET 4.5:
public class FilingPostModel
public string Id get; set;
public string TypeId get; set;
public string FirstName get; set;
public string MiddleName get; set;
public string LastName get; set;
public string Suffix get; set;
public string Line1 get; set;
public string Line2 get; set;
public string City get; set;
public string State get; set;
public string PostalCode get; set;
public string Country get; set;
public string Email get; set;
public string PhoneNumber get; set;
public string Comment get; set;
public string DateSubmitted get; set;
public string Summary get; set;
public List<FilePostModel> FileData get; set;
public class FilePostModel
public string FileId get; set;
public string FileName get; set;
public string ContentType get; set;
public string FileContent get; set;
public string DateSubmitted get; set;
public string ClassificationId get; set;
测试客户端正在提交此模型:
City: "j"
Comment: null
Country: "United States"
Email: "test@test.tst"
FileData: Count = 1
TypeId: "f94e264a-c8b1-44aa-862f-e6f0f7565e19"
FirstName: "fname"
Id: null
LastName: "lname"
Line1: "testdrive 1"
Line2: null
MiddleName: null
PhoneNumber: "3748923798"
PostalCode: "12345"
State: "Pennsylvania"
Suffix: null
Summary: null
FileData 组件有一项:
FileContent: "xY+v6sC8RHQ19av2LpyFGu6si8isrn8YquwGRAalW/6Q..."
ClassificationId: null
ContentType: "text/plain"
FileName: "large.txt"
这是用于创建和发送 API 请求的测试客户端方法
public async Task<ActionResult> PostNewFiling(FilingPostModel model)
Dictionary<string, string> req = new Dictionary<string, string>
"grant_type", "password",
"username", "some user name",
"password", "some password",
;
FilingApiPostModel postModel = new FilingApiPostModel(model);
using (HttpClient client = new HttpClient())
client.Timeout = TimeSpan.FromMinutes(15);
client.BaseAddress = new Uri(baseUrl);
var resp = await client.PostAsync("Token", new FormUrlEncodedContent(req));
if (resp.IsSuccessStatusCode)
TokenModel token = JsonConvert.DeserializeObject<TokenModel>(await resp.Content.ReadAsStringAsync());
if (!String.IsNullOrEmpty(token.access_token))
foreach (HttpPostedFileBase file in model.Files)
if (file != null)
FilePostModel fmodel = new FilePostModel();
fmodel.FileName = file.FileName;
fmodel.ContentType = file.ContentType;
byte[] fileData = new byte[file.ContentLength];
await file.InputStream.ReadAsync(fileData, 0, file.ContentLength);
fmodel.FileContent = Convert.ToBase64String(fileData);
fmodel.ClassificationId = model.Classification1;
postModel.FileData.Add(fmodel);
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token.access_token);
var response = await client.PostAsJsonAsync("api/Filing/PostFiling", postModel);
var responseBody = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
return Json(new responseBody );
else
return Json(new error = true, message = "Error Uploading", obj = responseBody );
return Json(new error = true, message = "Error Uploading" );
以下是接收此客户端请求的 API 方法:
public async Task<StatusModel> PostFiling(FilingPostModel model)
这是 web.config 中的 maxAllowedContentLength 设置:
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="4294967295" />
</requestFiltering>
</security>
</system.webServer>
在此测试场景中,API 模型始终为 null
。我收到两种类型的错误:
Newtonsoft.Json - 数组尺寸超出支持范围 Newtonsoft.Json - 解析值时遇到意外字符:x。路径 'FileData[0].Bytes',第 1 行,位置 517
此测试文件的文件大小为 560 MB。我使用Dummy File Creator 创建它。以下是内容的示例:
ůêÀ¼Dt5õ«ö.œ…Ȭ®ªìD¥[þ6\hW åz·cʾYP¸‡>•;,–@Ó¶ÿm™fø@ÃNÇIäÀ¿Y4~ëÆÃc¥EWÀ_÷õ9%«éÀG!WBÍ*G2P×æŸ7ú‚ÓêRúÅîµMZSªšpt6ä”Òø˜H
我也尝试使用“fsutil file createnew”创建测试文件,但收到类似错误。
使用 256 MB 文件进行测试时一切正常。
感谢您的帮助。
【问题讨论】:
第 1 行位置 517 上的字符是什么?看起来不是大小问题。代码在第一个坏字符上失败。 我更新了问题,提供了有关正在上传的文件的更多详细信息。 我建议更改您的模型以分离二进制数据和表单数据。这样您就可以将模型解析为 json 并单独处理数据,而且它的性能也会更高。 A sample 关于如何使用HttpClient
发送 multipart/form-data 内容
如@Eldar 所述,最好将表单数据和二进制数据分开。 byte []
二进制数据在 JSON 中表示为 JSON 中的 Base64 字符串,而 Json.NET 不具备读取“块”中的巨大字符串的能力。因此,如果二进制数据太大,您可能会超过MAX .Net array size。要改善问题,请参阅Json.Net deserialize out of memory issue。
服务器设置允许的最大请求大小是多少?
【参考方案1】:
如果问题是由数组的总大小而不是数组中的元素数量引起的,则可以通过定位 64 位编译和更新 app.config 来避免 OutOfMemoryException
:
<configuration>
<runtime>
<gcAllowVeryLargeObjects enabled="true" />
</runtime>
</configuration>
gcAllowVeryLargeObjects element
【讨论】:
【参考方案2】:每个实现都有自己的限制,例如 php 默认有 2MB,您可以使用 Post_Max_Size 增加它
ASP.NET Core 强制 30MB 大于默认限制的任何文件都可能导致错误。
这里是Upload Large Files In ASP.NET Core!
对于 IIS Express,您需要将 web.config 添加到您的项目中,并在其中添加以下标记:
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="209715200" />
</requestFiltering>
</security>
</system.webServer>
【讨论】:
【参考方案3】:您可以为您的操作添加两个属性:
[RequestFormLimits(MultipartBodyLengthLimit = 209715200)]
[FromBody]
【讨论】:
【参考方案4】:如果您需要发布 JSON,我会将您的 FileData 对象更改为使用 Base64 编码的字符串来代替字节数组。
将 FileData 中的 Bytes
属性更改为字符串,然后在创建请求时将 fmodel.Bytes = fileData
更改为 fmodel.Bytes = Convert.ToBase64String(fileData)
。
反序列化 JSON 后,您可以使用 Convert.FromBase64String(String)
将 base64 转换回字节[]。
这不仅可以防止您超出 JSON 中的数组限制,还可以大大减少负载大小。
【讨论】:
注意:与字节数组的 Json 表示相比,Base64'ing 可能会减小大小,但与字节数组相比则不会。 Base64 数据的大小约为原始数据的 4/3 这不起作用。 API 上的空模型存在同样的问题。我用 base64 字符串更改更新了问题以上是关于尝试接受具有大文件的模型时出现 .net API 错误的主要内容,如果未能解决你的问题,请参考以下文章
使用 Trainer API 预训练 BERT 模型时出现 ValueError
从 Ember 发布到 DRF API 时出现 406(不可接受)
创建具有大 (>1GB) BytesWritable 值大小的 SequenceFile 时出现 NegativeArraySizeException