我在ASP.NET Core Web API中的控制器中有一个上传和处理文件的操作。文件很小时没关系,但如果文件很大怎么办?我希望这个动作是异步执行的,所以我将方法定义为async
,它的返回值只是Task
是等待的,并在await Task.Run(() => ...)
块中插入连续操作,使动作也是async
并用await
关键字调用此方法,但实际上,调用此方法后,此操作不会返回。我试图上传大文件,不得不等到文件完全上传和处理。那么,我该怎么做才能使这个动作真的异步执行呢?
您的Web Api项目的调用方不知道C#细节,Task
是什么或await
的意思。它可能是C#或JS,甚至是PHP或在浏览器栏中键入您的URL的人。
您的Web Api默认情况下(并且应该!)等待整个处理完成,然后发出一个成功的信号。这通常通过状态代码作为返回值来完成。
现在调用API的客户端不必等待它。但这是客户端编程。你无法在API中做任何事情来解决客户端的问题。
否则,您可以让您的API将文件放在处理队列中,稍后由另一个软件处理。但这需要另一种架构,因为现在调用者需要在不再连接到API时通知失败成功(或导致一般),因此您需要回调。
我有这个代码用于处理大文件(这是一个大型CSV文件上传):
public async Task<IActionResult> UploadAsync(IFormFile file)
{
// Ensure the file has contents before processing.
if (file == null || file.Length == 0)
throw new ApiException("Csv file should not be null", HttpStatusCode.BadRequest)
.AddApiExceptionResponseDetails(ErrorTypeCode.ValidationError, ErrorCode.BelowMinimumLength, SOURCE);
// Ensure the file is not over the allowed limit.
if (file.Length > (_settings.MaxCsvFileSize * 1024))
throw new ApiException("Max file size exceeded, limit of " + _settings.MaxCsvFileSize + "mb", HttpStatusCode.BadRequest)
.AddApiExceptionResponseDetails(ErrorTypeCode.ValidationError, ErrorCode.ExceedsMaximumLength, SOURCE);
// Ensure the file type is csv and content type is correct for the file.
if (Path.GetExtension(file.FileName) != ".csv" ||
!Constants.CsvAcceptedContentType.Contains(file.ContentType.ToLower(CultureInfo.InvariantCulture)))
throw new ApiException("Csv content only accepted").AddApiExceptionResponseDetails(ErrorTypeCode.ValidationError, ErrorCode.Invalid, SOURCE);
// Read csv content.
var content = await file.ReadCsvAsync<OrderCsvResponseDto>() as CsvProcessedResponseDto<OrderCsvResponseDto>;
await ProcessBulkUpload(content);
// Return information about the csv file.
return Ok(content);
}
internal async Task ProcessBulkUpload(CsvProcessedResponseDto<OrderCsvResponseDto> content)
{
// do some processing...
}
有web.config设置可以增加文件上传的允许时间,这可能会有所帮助:How to increase the max upload file size in ASP.NET?
如果您的请求超过了允许的最大超时数,则不会按预期将数据返回给调用者!
如果要从C#执行“即发即弃”代码,可以执行以下操作:
public static void FireAndForget(this Task task)
{
Task.Run(async() => await task).ConfigureAwait(false);
}
使用Javascript:
xhr.onreadystatechange = function() { xhr.abort(); }
AngularJS:
var defer = $q.defer();
$http.get('/example', { timeout: defer.promise }).success(callback);
// [...]
defer.resolve();
一些异步/等待Js的提示:http://2ality.com/2016/10/async-function-tips.html