上传进度指示器以获取?
我很努力地找到使用fetch实现上传进度指示器的文档或示例。上传进度指示器以获取?
This is the only reference I've found so far,其中规定:
进度事件是一个高层次的功能,不会到达取现在。您可以通过查看
Content-Length
标题并使用传递流来监控收到的字节来创建您自己的标题。这意味着您可以明确处理响应,而不需要
Content-Length
。当然,即使在那里,它也可能是一个谎言。不过,你可以通过流处理这些谎言。
我该如何写“传递流来监控字节”发送?如果它有什么不同,我试图这样做,以从浏览器向Cloudinary上传图片。
注意:我不感兴趣的Cloudinary JS library,因为它依赖于jQuery和我的应用程序没有。我只对使用原生JavaScript和Github的fetch
polyfill进行流处理很感兴趣。
流开始登陆网络平台(https://jakearchibald.com/2016/streams-ftw/),但它仍然是早期。
很快,您将能够提供一个流作为请求的主体,但未解决的问题是该流的消耗是否与上传的字节有关。
特定的重定向可能会导致数据重新传输到新的位置,但流不能“重新启动”。我们可以通过将主体转换为可以多次调用的回调来解决这个问题,但是我们需要确保暴露重定向的数量不是安全漏洞,因为这将是平台上的第一次JS可以检测到。
有人质疑是否有必要将流消费链接到上传的字节。长话短说:这是不可能的,但在未来,这将通过流,或某种更高级别的回调传递到fetch()
处理。
我不认为这是可能的。状态草案:
它是目前所缺乏[相比XHR]当涉及到请求进展
(旧回答):
在第一个例子Fetch API chapter提供了一些有关如何:
如果要逐步接受身体数据:
function consume(reader) { var total = 0 return new Promise((resolve, reject) => { function pump() { reader.read().then(({done, value}) => { if (done) { resolve() return } total += value.byteLength log(`received ${value.byteLength} bytes (${total} bytes in total)`) pump() }).catch(reject) } pump() }) } fetch("/music/pk/altes-kamuffel.flac") .then(res => consume(res.body.getReader())) .then(() => log("consumed the entire body without keeping the whole thing in memory!")) .catch(e => log("something went wrong: " + e))
除了他们使用Promise
constructor antipattern的,你可以看到,response.body
是一个流,从中可以按字节使用Reader读取的字节,而你可以发起事件或做任何你喜欢的事情(例如,记录进度)为他们每个人。
但是,Streams spec似乎没有完全完成,我不知道这是否已经在任何获取实现工作。
但是,如果我正确地阅读了这个例子,这可能是**通过'fetch'下载**文件。我对**上传**文件的进度指示器感兴趣。 – neezer
哎呀,那引用关于*接收*字节的说法,这让我很困惑。 – Bergi
@Bergi注意,'Promise'构造函数是没有必要的。 'Response.body.getReader()'返回一个'Promise'。见[下载大尺寸json时如何解决Uncaught RangeError](http://*.com/questions/39959467/how-to-solve-uncaught-rangeerror-when-download-large-size-json) – guest271314
一个可能的解决办法是利用new Request()
构造函数,然后检查Request.bodyUsed
Boolean
属性
的
bodyUsed
属性的获取必须的,如果disturbed
返回true, 否则为false。
,以确定是否流是distributed
实施
Body
混入的对象被认为是disturbed
如果body
为非空并且其stream
disturbed
是。
返回的fetch()
Promise
从.then()
内链接到递归.read()
呼叫的ReadableStream
时Request.bodyUsed
等于true
。
请注意,该方法在字节流式传输到端点时不会读取Request.body
的字节。此外,在任何响应完全返回给浏览器之前,上传可能会完成。
const [input, progress, label] = [
document.querySelector("input")
, document.querySelector("progress")
, document.querySelector("label")
];
const url = "/path/to/server/";
input.onmousedown =() => {
label.innerHTML = "";
progress.value = "0"
};
input.onchange = (event) => {
const file = event.target.files[0];
const filename = file.name;
progress.max = file.size;
const request = new Request(url, {
method: "POST",
body: file,
cache: "no-store"
});
const upload = settings => fetch(settings);
const uploadProgress = new ReadableStream({
start(controller) {
console.log("starting upload, request.bodyUsed:", request.bodyUsed);
controller.enqueue(request.bodyUsed);
},
pull(controller) {
if (request.bodyUsed) {
controller.close();
}
controller.enqueue(request.bodyUsed);
console.log("pull, request.bodyUsed:", request.bodyUsed);
},
cancel(reason) {
console.log(reason);
}
});
const [fileUpload, reader] = [
upload(request)
.catch(e => {
reader.cancel();
throw e
})
, uploadProgress.getReader()
];
const processUploadRequest = ({value, done}) => {
if (value || done) {
console.log("upload complete, request.bodyUsed:", request.bodyUsed);
// set `progress.value` to `progress.max` here
// if not awaiting server response
// progress.value = progress.max;
return reader.closed.then(() => fileUpload);
}
console.log("upload progress:", value);
progress.value = +progress.value + 1;
return reader.read().then(result => processUploadRequest(result));
};
reader.read().then(({value, done}) => processUploadRequest({value,done}))
.then(response => response.text())
.then(text => {
console.log("response:", text);
progress.value = progress.max;
input.value = "";
})
.catch(err => console.log("upload error:", err));
}
由于没有一个答案解决问题。
只是为了实现,您可以检测上传速度with some small initial chunk of known size,上传时间可以通过content-length/upload-speed来计算。您可以使用这段时间作为估计。
在我们等待实时解决方案时使用非常聪明,好用的技巧:) – Magix
关键部分是ReadableStream ≪ obj_response。身体≫。
样品:
let parse=_/*result*/=>{
console.log(_)
//...
return /*cont?*/_.value?true:false
}
fetch('').
then(_=>(a/*!*/=_.body.getReader(), b/*!*/=z=>a.read().then(parse).then(_=>(_?b:z=>z)()), b()))
您可以测试一个巨大的页面例如https://html.spec.whatwg.org/和https://html.spec.whatwg.org/print.pdf运行它。 CtrlShiftJ和加载代码中
(测试镀铬。)
const req = await fetch('./foo.json');
const total = Number(req.headers.get('content-length'));
let loaded = 0;
for await(const {length} of req.body.getReader()) {
loaded = += length;
const progress = ((loaded/total) * 100).toFixed(2); // toFixed(2) means two digits after floating point
console.log(`${progress}%`); // or yourDiv.textContent = `${progress}%`;
}
这将不会工作,因为'ReadableStreamDefaultReader'没有'byteLength'属性。 – silicakes
我想给本杰明格鲁恩鲍姆一个完整的答案。因为我从他的演讲中学到了。 –
@Magix参见[中止取:下一代#447]。(HTTPS:// github上。com/whatwg/fetch/issues/447) – guest271314