首页 > 解决方案 > 在 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.

为了产生这样的问题/边缘案例,我尝试了:

当我运行调试模式时,在每种情况下,我都发现:UploadAppraisalFile从未到达端点,或者如果到达端点,则始终发送完整文件。对于第二个成功案例,为了 100% 确定我将接收到的文件转换为 base64 字符串,然后我检查了文件在https://codebeautify.org/base64-to-image-converter中的样子。

我的问题是:由于在发送过程中突然发生的互联网连接中断,发送的 POST 请求是否可能被破坏并且不包含完整的文件内容?如果是,那么产生问题的最佳方法是什么。干杯

标签: restasp.net-corepostfile-uploadconnection

解决方案


您可以部分传递HttpContext.RequestAbortedCancellationToken.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();
}

推荐阅读