I am making a Blazor client side app that has a file upload feature with a progress bar. The actual upload code is in javascript and it is just being invoked by the Blazor component. Here is the javascript code for upload:
(function (self) {
var uploaders = {};
self.initialize = function (componentId) {
uploaders[componentId] = new XMLHttpRequest();
console.log("Initialized...", componentId);
}
self.upload = function (componentId, fileId, file, url, contentType, instance, progressCallback, completeCallback) {
console.log("In upload event...", fileId, url);
var xhr = uploaders[componentId];
xhr.open("PUT", url, true);
xhr.upload.onloadstart = function (e) {
console.log("Upload started", e.total);
};
xhr.upload.onprogress = function (e) {
console.log("Progress report...", e.loaded);
var percentComplete = e.loaded / e.total * 100;
var data = {
fileId: fileId,
progress: percentComplete
};
instance.invokeMethodAsync(progressCallback, data);
};
xhr.onload = function (e) {
console.log("On uploaded...", e);
var data = {
fileId: fileId,
isSuccess: true
};
instance.invokeMethodAsync(completeCallback, data);
};
xhr.onerror = function (e) {
console.log("On error...", e);
var data = {
fileId: fileId,
isSuccess: false
};
// instance.invokeMethodAsync(completeCallback, data);
};
xhr.upload.onerror = function (e) {
console.log("On error...", e);
var data = {
fileId: fileId,
isSuccess: false
};
instance.invokeMethodAsync(completeCallback, data);
};
xhr.setRequestHeader("Content-Type", contentType);
xhr.withCredentials = false;
xhr.send(file);
}
self.destroy = function (componentId) {
var xhr = uploaders[componentId];
if (xhr) {
xhr.abort();
xhr = null;
}
console.log("Destroyed...", componentId);
}
})(window.clientPortal.fileUpload = window.clientPortal.fileUpload || {});
The Blazor component has the following methods to trigger upload and catch progress report:
private async Task StartUploadAsync(FileUploadStatus file, DocumentUploadUrl uploadData)
{
var bytes = await ToByteArray(file.Data);
await JS.InvokeVoidAsync("window.clientPortal.fileUpload.upload",
_uniqueIdentifier.ToString(),
file.FileId.ToString(),
bytes,
uploadData.Url,
uploadData.ContentType,
_objectReference,
nameof(OnUploadProgress),
nameof(OnUploadComplete));
}
[JSInvokable]
public void OnUploadProgress(UploadProgress response)
{
var match = FilesBeingUploaded.First(f => f.FileId == response.FileId);
match.UploadedPercentage = response.Progress;
StateHasChanged();
}
This all actually work fine after a hard browser page refresh (CTRL+F5), and it works for as long as I don’t do a soft page refresh. However once I soft refresh, the onprogress event handler never fires, as if it is not defined or not invoked anymore. The file upload still works but I have no progress reporting on it. No errors are raised, no visible issues of any kind. The file being upload is going to the AWS S3 storage so the URL of the file is always different – not being cached.
Has anyone experienced anything like this with the XMLHttpRequest? I am totally baffled by this issue…