fix #4598 - user experience improvements for file upload
This commit is contained in:
parent
044cee30a5
commit
69bc06685f
|
@ -387,7 +387,7 @@
|
||||||
|
|
||||||
var size = Int64.Parse(uploads[upload].Split(':')[1]); // bytes
|
var size = Int64.Parse(uploads[upload].Split(':')[1]); // bytes
|
||||||
var megabits = (size / 1048576.0) * 8; // binary conversion
|
var megabits = (size / 1048576.0) * 8; // binary conversion
|
||||||
var uploadspeed = 2; // 2 Mbps (3G ranges from 300Kbps to 3Mbps)
|
var uploadspeed = (PageState.Alias.Name.Contains("localhost")) ? 100 : 3; // 3 Mbps is FCC minimum for broadband upload
|
||||||
var uploadtime = (megabits / uploadspeed); // seconds
|
var uploadtime = (megabits / uploadspeed); // seconds
|
||||||
var maxattempts = 5; // polling (minimum timeout duration will be 5 seconds)
|
var maxattempts = 5; // polling (minimum timeout duration will be 5 seconds)
|
||||||
var sleep = (int)Math.Ceiling(uploadtime / maxattempts) * 1000; // milliseconds
|
var sleep = (int)Math.Ceiling(uploadtime / maxattempts) * 1000; // milliseconds
|
||||||
|
|
|
@ -425,11 +425,11 @@ namespace Oqtane.Controllers
|
||||||
// POST api/<controller>/upload
|
// POST api/<controller>/upload
|
||||||
[EnableCors(Constants.MauiCorsPolicy)]
|
[EnableCors(Constants.MauiCorsPolicy)]
|
||||||
[HttpPost("upload")]
|
[HttpPost("upload")]
|
||||||
public async Task UploadFile(string folder, IFormFile formfile)
|
public async Task<IActionResult> UploadFile(string folder, IFormFile formfile)
|
||||||
{
|
{
|
||||||
if (formfile == null || formfile.Length <= 0)
|
if (formfile == null || formfile.Length <= 0)
|
||||||
{
|
{
|
||||||
return;
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure filename is valid
|
// ensure filename is valid
|
||||||
|
@ -437,7 +437,7 @@ namespace Oqtane.Controllers
|
||||||
if (!formfile.FileName.IsPathOrFileValid() || !formfile.FileName.Contains(token) || !HasValidFileExtension(formfile.FileName.Substring(0, formfile.FileName.IndexOf(token))))
|
if (!formfile.FileName.IsPathOrFileValid() || !formfile.FileName.Contains(token) || !HasValidFileExtension(formfile.FileName.Substring(0, formfile.FileName.IndexOf(token))))
|
||||||
{
|
{
|
||||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "File Name Is Invalid Or Contains Invalid Extension {File}", formfile.FileName);
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "File Name Is Invalid Or Contains Invalid Extension {File}", formfile.FileName);
|
||||||
return;
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
string folderPath = "";
|
string folderPath = "";
|
||||||
|
@ -492,6 +492,8 @@ namespace Oqtane.Controllers
|
||||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Upload Attempt {Folder} {File}", folder, formfile.FileName);
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Upload Attempt {Folder} {File}", folder, formfile.FileName);
|
||||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> MergeFile(string folder, string filename)
|
private async Task<string> MergeFile(string folder, string filename)
|
||||||
|
|
|
@ -293,41 +293,49 @@ Oqtane.Interop = {
|
||||||
},
|
},
|
||||||
uploadFiles: function (posturl, folder, id, antiforgerytoken, jwt) {
|
uploadFiles: function (posturl, folder, id, antiforgerytoken, jwt) {
|
||||||
var fileinput = document.getElementById('FileInput_' + id);
|
var fileinput = document.getElementById('FileInput_' + id);
|
||||||
var files = fileinput.files;
|
|
||||||
var progressinfo = document.getElementById('ProgressInfo_' + id);
|
var progressinfo = document.getElementById('ProgressInfo_' + id);
|
||||||
var progressbar = document.getElementById('ProgressBar_' + id);
|
var progressbar = document.getElementById('ProgressBar_' + id);
|
||||||
|
|
||||||
if (progressinfo !== null && progressbar !== null) {
|
if (progressinfo !== null && progressbar !== null) {
|
||||||
progressinfo.setAttribute("style", "display: inline;");
|
progressinfo.setAttribute("style", "display: inline;");
|
||||||
|
progressinfo.innerHTML = '';
|
||||||
progressbar.setAttribute("style", "width: 100%; display: inline;");
|
progressbar.setAttribute("style", "width: 100%; display: inline;");
|
||||||
|
progressbar.value = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var files = fileinput.files;
|
||||||
|
var totalSize = 0;
|
||||||
|
for (var i = 0; i < files.length; i++) {
|
||||||
|
totalSize = totalSize + files[i].size;
|
||||||
|
}
|
||||||
|
|
||||||
|
var maxChunkSizeMB = 1;
|
||||||
|
var bufferChunkSize = maxChunkSizeMB * (1024 * 1024);
|
||||||
|
var uploadedSize = 0;
|
||||||
|
|
||||||
for (var i = 0; i < files.length; i++) {
|
for (var i = 0; i < files.length; i++) {
|
||||||
var FileChunk = [];
|
var fileChunk = [];
|
||||||
var file = files[i];
|
var file = files[i];
|
||||||
var MaxFileSizeMB = 1;
|
var fileStreamPos = 0;
|
||||||
var BufferChunkSize = MaxFileSizeMB * (1024 * 1024);
|
var endPos = bufferChunkSize;
|
||||||
var FileStreamPos = 0;
|
|
||||||
var EndPos = BufferChunkSize;
|
|
||||||
var Size = file.size;
|
|
||||||
|
|
||||||
while (FileStreamPos < Size) {
|
while (fileStreamPos < file.size) {
|
||||||
FileChunk.push(file.slice(FileStreamPos, EndPos));
|
fileChunk.push(file.slice(fileStreamPos, endPos));
|
||||||
FileStreamPos = EndPos;
|
fileStreamPos = endPos;
|
||||||
EndPos = FileStreamPos + BufferChunkSize;
|
endPos = fileStreamPos + bufferChunkSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
var TotalParts = FileChunk.length;
|
var totalParts = fileChunk.length;
|
||||||
var PartCount = 0;
|
var partCount = 0;
|
||||||
|
|
||||||
while (Chunk = FileChunk.shift()) {
|
while (chunk = fileChunk.shift()) {
|
||||||
PartCount++;
|
partCount++;
|
||||||
var FileName = file.name + ".part_" + PartCount.toString().padStart(3, '0') + "_" + TotalParts.toString().padStart(3, '0');
|
var fileName = file.name + ".part_" + partCount.toString().padStart(3, '0') + "_" + totalParts.toString().padStart(3, '0');
|
||||||
|
|
||||||
var data = new FormData();
|
var data = new FormData();
|
||||||
data.append('__RequestVerificationToken', antiforgerytoken);
|
data.append('__RequestVerificationToken', antiforgerytoken);
|
||||||
data.append('folder', folder);
|
data.append('folder', folder);
|
||||||
data.append('formfile', Chunk, FileName);
|
data.append('formfile', chunk, fileName);
|
||||||
var request = new XMLHttpRequest();
|
var request = new XMLHttpRequest();
|
||||||
request.open('POST', posturl, true);
|
request.open('POST', posturl, true);
|
||||||
if (jwt !== "") {
|
if (jwt !== "") {
|
||||||
|
@ -335,28 +343,36 @@ Oqtane.Interop = {
|
||||||
request.withCredentials = true;
|
request.withCredentials = true;
|
||||||
}
|
}
|
||||||
request.upload.onloadstart = function (e) {
|
request.upload.onloadstart = function (e) {
|
||||||
if (progressinfo !== null && progressbar !== null) {
|
if (progressinfo !== null && progressbar !== null && progressinfo.innerHTML === '') {
|
||||||
progressinfo.innerHTML = file.name + ' 0%';
|
if (files.length === 1) {
|
||||||
progressbar.value = 0;
|
progressinfo.innerHTML = file.name;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
progressinfo.innerHTML = file.name + ", ...";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
request.upload.onprogress = function (e) {
|
request.upload.onprogress = function (e) {
|
||||||
if (progressinfo !== null && progressbar !== null) {
|
if (progressinfo !== null && progressbar !== null) {
|
||||||
var percent = Math.ceil((e.loaded / e.total) * 100);
|
var percent = Math.ceil(((uploadedSize + e.loaded) / totalSize) * 100);
|
||||||
progressinfo.innerHTML = file.name + '[' + PartCount + '] ' + percent + '%';
|
|
||||||
progressbar.value = (percent / 100);
|
progressbar.value = (percent / 100);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
request.upload.onloadend = function (e) {
|
request.upload.onloadend = function (e) {
|
||||||
if (progressinfo !== null && progressbar !== null) {
|
if (progressinfo !== null && progressbar !== null) {
|
||||||
progressinfo.innerHTML = file.name + ' 100%';
|
uploadedSize = uploadedSize + e.total;
|
||||||
progressbar.value = 1;
|
var percent = Math.ceil((uploadedSize / totalSize) * 100);
|
||||||
|
progressbar.value = (percent / 100);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
request.upload.onerror = function() {
|
request.upload.onerror = function() {
|
||||||
if (progressinfo !== null && progressbar !== null) {
|
if (progressinfo !== null && progressbar !== null) {
|
||||||
|
if (files.length === 1) {
|
||||||
progressinfo.innerHTML = file.name + ' Error: ' + request.statusText;
|
progressinfo.innerHTML = file.name + ' Error: ' + request.statusText;
|
||||||
progressbar.value = 0;
|
}
|
||||||
|
else {
|
||||||
|
progressinfo.innerHTML = ' Error: ' + request.statusText;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
request.send(data);
|
request.send(data);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user