First, create a singleton service to hold the upload ID and its progress. Then, whenever you call the main upload API, update its progress. This way, you can retrieve the progress of each upload by its ID using the service.
Take a look at this sample code...
public interface IUploadProgressProvider
{
void UpdateProgress(string id, double progress);
double? GetProgress(string id);
void Remove(string id);
IEnumerable<(string Id, double Progress)> Info();
}
public sealed class UploadProgressProvider : IUploadProgressProvider
{
private ConcurrentDictionary<string, double> _uploadProgresses = new(StringComparer.InvariantCultureIgnoreCase);
public double? GetProgress(string id) => _uploadProgresses.TryGetValue(id, out var r) ? r : null;
public void Remove(string id) => _uploadProgresses.TryRemove(id, out var r);
public void UpdateProgress(string id, double progress) => _uploadProgresses.AddOrUpdate(id, progress, (id, p) => progress);
public IEnumerable<(string Id, double Progress)> Info() => this._uploadProgresses.Select(x => (x.Key, x.Value)).ToList();
}
Route("api/Upload")]
public sealed class UploadController : Controller
{
private readonly IUploadProgressProvider _uploadProgressProvider;
public UploadController(IUploadProgressProvider uploadProgressProvider)
{
_uploadProgressProvider = uploadProgressProvider;
}
[HttpPost("Upload/{id}")]
public async Task<IActionResult> Index(IList<IFormFile> files, string id)
{
long totalBytes = files.Sum(f => f.Length);
foreach (IFormFile source in files)
{
string filename = ContentDispositionHeaderValue.Parse(source.ContentDisposition).FileName.Trim('"');
byte[] buffer = new byte[16 * 1024];
using (FileStream output = System.IO.File.Create(this.GetPathAndFilename(filename)))
{
using (Stream input = source.OpenReadStream())
{
long totalReadBytes = 0;
int readBytes;
while ((readBytes = input.Read(buffer, 0, buffer.Length)) > 0)
{
await output.WriteAsync(buffer, 0, readBytes);
totalReadBytes += readBytes;
_uploadProgressProvider.UpdateProgress(id, ((float)totalReadBytes / (float)totalBytes * 100.0));
await Task.Delay(100); // It is only to make the process slower
}
}
}
}
_uploadProgressProvider.UpdateProgress(id, 100);
return this.Content("success");
}
[HttpGet("info")]
public async Task<IActionResult> Info() => this.Ok(this._uploadProgressProvider.Info());
}