我想将文件的上传进度显示到请求从服务器完成的时间。
那里的每一个例子都展示了如何进行一个进度条,用于将文件从浏览器发送到服务器而不是从浏览器发送到服务器回到浏览器。
下面的代码仅显示了客户端将文件上传到服务器而不是响应的进度。基本上,只要在服务器上发出请求,客户端上的进度事件就会完成,这不是我想要的。
我该怎么做?
XHR:
export default (url, opts = {}, uploadEvents) =>
new Promise((res, rej) => {
const xhr = new XMLHttpRequest();
xhr.open(opts.method || 'get', url, true);
Object.keys(opts.headers || {}).forEach((key) => {
xhr.setRequestHeader(key, opts.headers[key]);
});
xhr.onload = e => res(e.target.responseText);
xhr.onerror = rej;
if (xhr.upload) {
Object.keys(uploadEvents).forEach((key) => {
xhr.upload.addEventListener(key, uploadEvents[key]);
});
}
xhr.send(opts.body);
});
请求:
fetchProgress('/upload/upload', {
method: 'post',
body: formData,
}, {
progress: (e) => {
if (e.lengthComputable) {
const progressPercent = parseInt((e.loaded / e.total) * 100, 10);
progressPercents[i] = {
value: progressPercent,
id,
};
dispatch({
type: 'ADD_UPLOAD_PROGRESS',
progressPercents,
});
}
},
});
服务器代码:
[HttpPost]
public async Task<IActionResult> Upload(IFormFile file)
{
var processAudio = await _fileStorage.TempStoreMp3Data(file);
var audioBlob = _cloudStorage.GetBlob(CloudStorageType.Audio, processAudio.AudioName);
await audioBlob.UploadFromPathAsync(processAudio.AudioPath, ProcessAudio.AudioContentType);
return Ok();
}
答案 0 :(得分:2)
基本上,只要在服务器上发出请求,客户端上的进度事件就会完成,这不是我想要的。
要根据服务器端操作获取进度,我建议您创建另一个可以在服务器端返回进度的操作。在客户端,您需要调用此操作来获取进度。以下代码供您参考。
在服务器端,我们需要定义一个Dictionary来存储进度,我们需要使用uploadId来连接这两个动作。当调用这两个动作时,我们需要从客户端使用相同的uploadId。
//use this dictionary to store the progress for upload
private static Dictionary<string, int> uploadProgresses = new Dictionary<string, int>();
public IActionResult GetUploadProgress(string uploadId)
{
int progress = 0;
uploadProgresses.TryGetValue(uploadId, out progress);
return Content(progress.ToString());
}
[HttpPost]
public async Task<IActionResult> Upload(IFormFile file, string uploadId)
{
uploadProgresses.Add(uploadId, 0);
var processAudio = await _fileStorage.TempStoreMp3Data(file);
uploadProgresses[uploadId] = 20;
var audioBlob = _cloudStorage.GetBlob(CloudStorageType.Audio, processAudio.AudioName);
uploadProgresses[uploadId] = 40;
await audioBlob.UploadFromPathAsync(processAudio.AudioPath, ProcessAudio.AudioContentType);
uploadProgresses[uploadId] = 100;
return Ok();
}
客户端代码示例。
var myVar = window.setInterval(function () { getUploadProgress(); }, 1000);
var uniqualId = uuidv4();
document.getElementById("uuid").innerHTML = uniqualId;
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
function getUploadProgress() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
//update your progress bar
document.getElementById("progress").innerHTML = xhttp.responseText;
if (xhttp.responseText == '100') {
myStopFunction();
}
}
else if (this.readyState == 4 && this.status != 200)
{
myStopFunction();
}
};
xhttp.open("GET", "/Home/GetUploadProgress?uploadId=" + uniqualId, true);
xhttp.send();
}
function myStopFunction() {
window.clearInterval(myVar);
}
更新7/10/2017
我们是否应该使用Session来存储上传进度而不是静态字典?
不,我们无法使用Session来存储上传进度。根据ASP.NET应用程序的生命周期,状态(Session)将在执行处理程序(您的Action)后更新。这意味着在Action执行完成之前,Session不会更新。
答案 1 :(得分:0)
我找到了更好的方法:
为要更新其进度的每个进程创建一个操作。这样我们就可以完成客户端的整个进度,而不会在服务器上乱搞会话或静态变量。
示例:
[HttpPost]
public async Task<IActionResult> TempStoreMp3File(IFormFile file)
{
var processAudio = await _fileStorage.TempStoreMp3Data(file);
return Ok(new
{
audioName = processAudio.AudioName,
audioPath = processAudio.AudioPath,
});
}
[HttpPost]
public async Task<IActionResult> Upload([FromBody] UploadViewModel model)
{
var audioBlob = _cloudStorage.GetBlob(CloudStorageType.Audio, model.AudioName);
await audioBlob.UploadFromPathAsync(model.AudioPath, ProcessAudio.AudioContentType);
return Ok();
}
fetchProgress('/upload/tempStoreMp3File', {
method: 'post',
body: formData,
}, {
readystatechange() {
if (this.readyState === 4) {
dispatch({
type: 'UPDATE_UPLOAD_PROGRESS',
progressPercent: 75,
index: progressIndex,
message: 'Uploading to SoundVast...',
});
fetch('/upload/upload', {
method: 'post',
headers: {
'Content-Type': 'application/json',
},
body: this.responseText,
}).then((response) => {
if (response.ok) {
dispatch({
type: 'UPDATE_UPLOAD_PROGRESS',
progressPercent: 100,
index: progressIndex,
message: 'Successfully uploaded file.',
});
}
});
}
},
}, {
load: () => {
dispatch({
type: 'UPDATE_UPLOAD_PROGRESS',
progressPercent: 50,
index: progressIndex,
message: 'Converting to mp3...',
});
},
progress: (e) => {
if (e.lengthComputable) {
const progressPercent = parseInt((e.loaded / e.total) * 25, 10);
dispatch({
type: 'UPDATE_UPLOAD_PROGRESS',
progressPercent,
index: progressIndex,
message: 'Sending file to server...',
});
}
},
});
fetchProgress:
export default (url, opts = {}, events, uploadEvents) =>
new Promise((res, rej) => {
const xhr = new XMLHttpRequest();
xhr.open(opts.method || 'get', url, true);
Object.keys(opts.headers || {}).forEach((key) => {
xhr.setRequestHeader(key, opts.headers[key]);
});
xhr.onload = e => res(e.target.responseText);
xhr.onerror = rej;
Object.keys(events).forEach((key) => {
xhr.addEventListener(key, events[key]);
});
if (xhr.upload) {
Object.keys(uploadEvents).forEach((key) => {
xhr.upload.addEventListener(key, uploadEvents[key]);
});
}
xhr.send(opts.body);
});