Javascript 上传进度指示器以进行提取?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/35711724/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Upload progress indicators for fetch?
提问by neezer
I'm struggling to find documentation or examples of implementing an upload progress indicator using fetch.
我正在努力寻找使用fetch实现上传进度指示器的文档或示例。
This is the only reference I've found so far, which states:
这是迄今为止我找到的唯一参考,其中指出:
Progress events are a high level feature that won't arrive in fetch for now. You can create your own by looking at the
Content-Length
header and using a pass-through stream to monitor the bytes received.This means you can explicitly handle responses without a
Content-Length
differently. And of course, even ifContent-Length
is there it can be a lie. With streams you can handle these lies however you want.
进度事件是一项高级功能,目前不会在 fetch 中到达。您可以通过查看
Content-Length
标头并使用传递流来监视接收到的字节来创建自己的。这意味着您可以明确地处理响应而没有
Content-Length
不同。当然,即使Content-Length
存在也可能是谎言。通过流,您可以随心所欲地处理这些谎言。
How would I write "a pass-through stream to monitor the bytes" sent? If it makes any sort of difference, I'm trying to do this to power image uploads from the browser to Cloudinary.
我将如何编写“用于监视字节的传递流”发送?如果它有任何不同,我正在尝试这样做以支持从浏览器到Cloudinary 的图像上传。
NOTE: I am notinterested in the Cloudinary JS library, as it depends on jQuery and my app does not. I'm only interested in the stream processing necessary to do this with native javascript and Github's fetch
polyfill.
注意:我对Cloudinary JS 库不感兴趣,因为它依赖于 jQuery 而我的应用程序没有。我只对使用原生 javascript 和 Github 的polyfill执行此操作所需的流处理感兴趣。fetch
采纳答案by JaffaTheCake
Streams are starting to land in the web platform (https://jakearchibald.com/2016/streams-ftw/) but it's still early days.
Streams 开始登陆网络平台 ( https://jakearchibald.com/2016/streams-ftw/),但仍为时尚早。
Soon you'll be able to provide a stream as the body of a request, but the open question is whether the consumption of that stream relates to bytes uploaded.
很快您就可以提供一个流作为请求的主体,但悬而未决的问题是该流的消耗是否与上传的字节有关。
Particular redirects can result in data being retransmitted to the new location, but streams cannot "restart". We can fix this by turning the body into a callback which can be called multiple times, but we need to be sure that exposing the number of redirects isn't a security leak, since it'd be the first time on the platform JS could detect that.
特定的重定向可能会导致数据重新传输到新位置,但流无法“重新启动”。我们可以通过将主体转换为可以多次调用的回调来解决这个问题,但是我们需要确保暴露重定向次数不是安全漏洞,因为这是平台上的第一次 JS 可以检测到。
Some are questioning whether it even makes sense to link stream consumption to bytes uploaded.
有些人质疑将流消耗与上传的字节数联系起来是否有意义。
Long story short: this isn't possible yet, but in future this will be handled either by streams, or some kind of higher-level callback passed into fetch()
.
长话短说:这还不可能,但将来这将通过流或某种更高级别的回调传递到fetch()
.
回答by dwjohnston
My solution is to use axios, which supports this pretty well:
我的解决方案是使用axios,它很好地支持这一点:
axios.request( {
method: "post",
url: "/aaa",
data: myData,
onUploadProgress: (p) => {
console.log(p);
//this.setState({
//fileprogress: p.loaded / p.total
//})
}
}).then (data => {
//this.setState({
//fileprogress: 1.0,
//})
})
I have example for using this in react on github.
回答by Bergi
I don't think it's possible. The draft states:
我不认为这是可能的。草案指出:
it is currently lacking [in comparison to XHR] when it comes to request progression
在请求进度方面,它目前缺乏 [与 XHR 相比]
(old answer):
The first example in the Fetch API chaptergives some insight on how to :
(旧答案):Fetch API 章节中
的第一个示例提供了一些有关如何:
If you want to receive the body data progressively:
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))
如果要逐步接收正文数据:
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))
Apart from their use of the Promise
constructor antipattern, you can see that response.body
is a Stream from which you can read byte by byte using a Reader, and you can fire an event or do whatever you like (e.g. log the progress) for every of them.
除了它们对Promise
构造函数 antipattern的使用之外,您还可以看到这response.body
是一个 Stream,您可以使用 Reader 从中逐字节读取,并且您可以为每个事件触发一个事件或执行任何您喜欢的操作(例如记录进度)。
However, the Streams specdoesn't appear to be quite finished, and I have no idea whether this already works in any fetch implementation.
然而,Streams 规范似乎还没有完全完成,我不知道这是否已经在任何 fetch 实现中起作用。
回答by Hosseinmp76
Update: as the accepted answer says it's impossible now. but the below code handled our problem for sometime. I should add that at least we had to switch to using a library that is based on XMLHttpRequest.
更新:正如接受的答案所说,现在不可能了。但下面的代码处理了我们的问题一段时间。我应该补充一点,至少我们必须切换到使用基于 XMLHttpRequest 的库。
const response = await fetch(url);
const total = Number(response.headers.get('content-length'));
const reader = response.body.getReader();
let bytesReceived = 0;
while (true) {
const result = await reader.read();
if (result.done) {
console.log('Fetch complete');
break;
}
bytesReceived += result.value.length;
console.log('Received', bytesReceived, 'bytes of data so far');
}
thanks to this link: https://jakearchibald.com/2016/streams-ftw/
回答by Shishir Arora
Since none of the answers solve the problem.
因为没有一个答案能解决问题。
Just for implementation sake, you can detect the upload speed with some small initial chunk of known sizeand the upload time can be calculated with content-length/upload-speed. You can use this time as estimation.
只是为了实现,您可以使用一些已知大小的小初始块来检测上传速度,并且可以使用内容长度/上传速度计算上传时间。您可以将这个时间用作估计。
回答by guest271314
A possible workaround would be to utilize new Request()
constructor then check Request.bodyUsed
Boolean
attribute
一种可能的解决方法是利用new Request()
构造函数然后检查Request.bodyUsed
Boolean
属性
The
bodyUsed
attribute's getter must return true ifdisturbed
, and false otherwise.
该
bodyUsed
属性的getter方法必须返回true,如果disturbed
,否则为假。
to determine if stream is distributed
确定流是否为 distributed
An object implementing the
Body
mixin is said to bedisturbed
ifbody
is non-null and itsstream
isdisturbed
.
实现
Body
mixin的对象被称为disturbed
ifbody
非空且其stream
isdisturbed
。
Return the fetch()
Promise
from within .then()
chained to recursive .read()
call of a ReadableStream
when Request.bodyUsed
is equal to true
.
返回fetch()
Promise
from .then()
inside 链接到递归.read()
调用ReadableStream
whenRequest.bodyUsed
等于true
。
Note, the approach does not read the bytes of the Request.body
as the bytes are streamed to the endpoint. Also, the upload could complete well before any response is returned in full to the browser.
请注意,该方法不会读取 的字节,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));
}
回答by Leon Gilyadov
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}%`;
}
回答by Pacerier
Key part is ReadableStream≪obj_response.body≫.
关键部分是ReadableStream≪ obj_response.body≫。
Sample:
样本:
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() ))
You can test running it on a huge page eg https://html.spec.whatwg.org/and https://html.spec.whatwg.org/print.pdf. CtrlShiftJ and load the code in.
您可以在大页面上测试运行它,例如https://html.spec.whatwg.org/和https://html.spec.whatwg.org/print.pdf。CtrlShiftJ 并加载代码。
(Tested on Chrome.)
(在 Chrome 上测试。)