rest - 在 asp.net core MVC 中通过 API POST 端点处理未完成的文件上传
问题描述
为了简化问题,假设我有一个简单的 asp.net mvc 端点,它接收一个文件。在大多数情况下,它将是.jpg
一个:
[HttpPost]
[Route("appraisal/{appraisalID}/files/{fileSubjectCode}")]
[ProducesResponseType(StatusCodes.Status201Created, Type = typeof(IEnumerable<AppraisalFileModel>))]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ModelStateDictionary))]
public async Task<IActionResult> UploadAppraisalFile(int appraisalID, string fileSubjectCode, [FromForm]IFormFile file)
{
file = file ?? Request.Form.Files?.FirstOrDefault();
// intermitent code change to investigate and validate Complete File Size and Actual File Size
var completeFileSizeHeader = Request.Headers["Complete-File-Size"];
int.TryParse(completeFileSizeHeader, out int completeFileSize);
if (file == null || file.Length != completeFileSize)
{
using (var stream = new MemoryStream())
{
await file.CopyToAsync(stream);
stream.Position = 0;
var inputAsString = Convert.ToBase64String(stream.ToArray());
Logger.LogDebug("Complete-File-Size header doesn't much received byteArray size", file.Length, completeFileSize, inputAsString);
}
return StatusCode(StatusCodes.Status411LengthRequired, "Complete-File-Size header doesn't much received byteArray size");
}
// some other logic..
}
当有人对我的 API 端点执行 POST 请求并突然失去互联网连接时,我试图防止出现边缘情况,UploadAppraisalFile
这将导致发送不包含完整文件内容的请求。我的想法是在文件上传时计算文件大小,将有关文件大小的信息添加为额外的 HTTP-HEADER(我称之为Complete-File-Size
),然后当请求到达后端时,计算接收到的文件大小与Complete-File-Size
.
为了产生这样的问题/边缘案例,我尝试了:
- 上传一个大文件(大约 16MB)然后在提交 HTML 表单后突然关闭浏览器窗口。
- 上传文件,然后在 Chrome 浏览器的“网络”窗格中,将上传速度更改为最低,然后提交表单,然后立即关闭浏览器。
当我运行调试模式时,在每种情况下,我都发现:UploadAppraisalFile
从未到达端点,或者如果到达端点,则始终发送完整文件。对于第二个成功案例,为了 100% 确定我将接收到的文件转换为 base64 字符串,然后我检查了文件在https://codebeautify.org/base64-to-image-converter中的样子。
我的问题是:由于在发送过程中突然发生的互联网连接中断,发送的 POST 请求是否可能被破坏并且不包含完整的文件内容?如果是,那么产生问题的最佳方法是什么。干杯
解决方案
您可以部分传递HttpContext.RequestAborted
给CancellationToken
.NET 提供的所有异步方法"some other logic"
。
让我们以您提供的代码为例:
await stream.CopyToAsync(memoryStream, HttpContext.RequestAborted)
我无权访问完整的方法,但我假设您将其保存到某个 blob 存储或文件系统。大多数这些持久性 API 都接受CancellationToken
作为参数。
收到不完整的文件
我能够使用此代码和 Postman 实现“部分”文件。它基本上会从响应流中读取块,直到连接中断。一旦我关闭邮递员窗口TaskCancelledException
就会升起并关闭流。
[HttpPost]
public async Task<IActionResult> UploadAppraisalFile([FromForm] IFormFile file)
{
var appraisalfile = file ?? Request.Form.Files.FirstOrDefault();
if (appraisalfile != null)
{
var buffer = ArrayPool<byte>.Shared.Rent(1024);
using var stream = appraisalfile.OpenReadStream();
while (await stream.ReadAsync(buffer, 0, buffer.Length, HttpContext.RequestAborted) > 0)
{
// Do something with buffer
_logger.LogInformation("Total length (bytes) {0}, partial length (bytes) {1}", stream.Length, stream.Position);
}
ArrayPool<byte>.Shared.Return(buffer);
}
return Ok();
}
推荐阅读
- javascript - 如何获取 Float64Array 的长度作为值?
- javascript - 如何仅获取对象的名称并将其动态转换为字符串?
- xcode - 将 UIImage 从 UITableViewCell 传递到下一个 ViewController
- python - 熊猫清理
- apache-kafka - Kafks consumer.poll 不返回任何数据
- google-sheets - 根据下拉列表中的选择应用不同的公式
- r - 在 R 中的数据框中创建条件序列
- asp.net-core - 从 lambda 授权方响应中提取身份
- html - 将css应用于单个元素,类不起作用
- mysql - Innodb 中的 MySQL min() 行为