nee*_*zer 74 javascript html5 fetch-api
我很难找到使用fetch实现上传进度指示器的文档或示例.
这是迄今为止我发现的唯一参考资料,其中指出:
进度事件是一个高级功能,暂时无法获取.您可以通过查看
Content-Length标头并使用传递流来监视接收的字节来创建自己的标头.这意味着您可以无需另外明确地处理响应
Content-Length.当然,即使在Content-Length那里也可能是谎言.使用流可以根据需要处理这些谎言.
如何编写"传输流来监控字节"发送?如果它产生任何差异,我正在尝试这样做以支持从浏览器到Cloudinary的图像上传.
注意:我对Cloudinary JS库不感兴趣,因为它依赖于jQuery而我的应用程序没有.我只对使用原生javascript和Github的polyfill 执行此操作所需的流处理感兴趣.fetch
jtb*_*des 69
浏览器正在努力支持ReadableStream作为 fetch body。对于 Chrome,这是从 v105 开始实施的。对于其他浏览器,目前尚未实现。
(请注意,duplex: "half"当前需要使用带有 fetch 的流主体。)
自定义TransformStream可用于跟踪进度。这是一个工作示例:
\n警告:此代码不适用于 Chrome 以外的浏览器
\nasync function main() {\n const blob = new Blob([new Uint8Array(10 * 1024 * 1024)]); // any Blob, including a File\n const uploadProgress = document.getElementById("upload-progress");\n const downloadProgress = document.getElementById("download-progress");\n\n const totalBytes = blob.size;\n let bytesUploaded = 0;\n\n // Use a custom TransformStream to track upload progress\n const progressTrackingStream = new TransformStream({\n transform(chunk, controller) {\n controller.enqueue(chunk);\n bytesUploaded += chunk.byteLength;\n console.log("upload progress:", bytesUploaded / totalBytes);\n uploadProgress.value = bytesUploaded / totalBytes;\n },\n flush(controller) {\n console.log("completed stream");\n },\n });\n const response = await fetch("https://httpbin.org/put", {\n method: "PUT",\n headers: {\n "Content-Type": "application/octet-stream"\n },\n body: blob.stream().pipeThrough(progressTrackingStream),\n duplex: "half",\n });\n \n // After the initial response headers have been received, display download progress for the response body\n let success = true;\n const totalDownloadBytes = response.headers.get("content-length");\n let bytesDownloaded = 0;\n const reader = response.body.getReader();\n while (true) {\n try {\n const { value, done } = await reader.read();\n if (done) {\n break;\n }\n bytesDownloaded += value.length;\n if (totalDownloadBytes != undefined) {\n console.log("download progress:", bytesDownloaded / totalDownloadBytes);\n downloadProgress.value = bytesDownloaded / totalDownloadBytes;\n } else {\n console.log("download progress:", bytesDownloaded, ", unknown total");\n }\n } catch (error) {\n console.error("error:", error);\n success = false;\n break;\n }\n }\n \n console.log("success:", success);\n}\nmain().catch(console.error);Run Code Online (Sandbox Code Playgroud)\r\nupload: <progress id="upload-progress"></progress><br/>\ndownload: <progress id="download-progress"></progress>Run Code Online (Sandbox Code Playgroud)\r\n相反fetch(),可以使用XMLHttpRequest来跟踪上传进度\xe2\x80\x94xhr.upload对象发出一个progress事件。
async function main() {\n const blob = new Blob([new Uint8Array(10 * 1024 * 1024)]); // any Blob, including a File\n const uploadProgress = document.getElementById("upload-progress");\n const downloadProgress = document.getElementById("download-progress");\n\n const xhr = new XMLHttpRequest();\n const success = await new Promise((resolve) => {\n xhr.upload.addEventListener("progress", (event) => {\n if (event.lengthComputable) {\n console.log("upload progress:", event.loaded / event.total);\n uploadProgress.value = event.loaded / event.total;\n }\n });\n xhr.addEventListener("progress", (event) => {\n if (event.lengthComputable) {\n console.log("download progress:", event.loaded / event.total);\n downloadProgress.value = event.loaded / event.total;\n }\n });\n xhr.addEventListener("loadend", () => {\n resolve(xhr.readyState === 4 && xhr.status === 200);\n });\n xhr.open("PUT", "https://httpbin.org/put", true);\n xhr.setRequestHeader("Content-Type", "application/octet-stream");\n xhr.send(blob);\n });\n console.log("success:", success);\n}\nmain().catch(console.error);Run Code Online (Sandbox Code Playgroud)\r\nupload: <progress id="upload-progress"></progress><br/>\ndownload: <progress id="download-progress"></progress>Run Code Online (Sandbox Code Playgroud)\r\nJaf*_*ake 36
Streams开始登陆网络平台(https://jakearchibald.com/2016/streams-ftw/),但现在还处于早期阶段.
很快你就能够提供一个流作为请求的主体,但是开放的问题是该流的消耗是否与上传的字节有关.
特定重定向可能导致数据重新传输到新位置,但流不能"重新启动".我们可以通过将主体转换为可以多次调用的回调来解决这个问题,但我们需要确保暴露重定向的数量不是安全漏洞,因为它是第一次在JS平台上检测到.
有些人质疑将流消耗链接到上传的字节是否有意义.
长话短说:这还不可能,但将来这将由流处理,或者传递给某种更高级别的回调fetch().
Ric*_*Dev 25
正如其他答案中已经解释的那样,使用 是不可能的fetch,但是使用 XHR 是不可能的。这是我的更紧凑的 XHR 解决方案:
const uploadFiles = (url, files, onProgress) =>
new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', e => onProgress(e.loaded / e.total));
xhr.addEventListener('load', () => resolve({ status: xhr.status, body: xhr.responseText }));
xhr.addEventListener('error', () => reject(new Error('File upload failed')));
xhr.addEventListener('abort', () => reject(new Error('File upload aborted')));
xhr.open('POST', url, true);
const formData = new FormData();
Array.from(files).forEach((file, index) => formData.append(index.toString(), file));
xhr.send(formData);
});
Run Code Online (Sandbox Code Playgroud)
适用于一个或多个文件。
如果您有这样的文件输入元素:
<input type="file" multiple id="fileUpload" />
Run Code Online (Sandbox Code Playgroud)
像这样调用该函数:
document.getElementById('fileUpload').addEventListener('change', async e => {
const onProgress = progress => console.log('Progress:', `${Math.round(progress * 100)}%`);
const response = await uploadFiles('/api/upload', e.currentTarget.files, onProgress);
if (response.status >= 400) {
throw new Error(`File upload failed - Status code: ${response.status}`);
}
console.log('Response:', response.body);
}
Run Code Online (Sandbox Code Playgroud)
也适用于构建文件放置区域时e.dataTransfer.files从事件中获得的信息。drop
dwj*_*ton 16
我的解决方案是使用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,
//})
})
Run Code Online (Sandbox Code Playgroud)
Hos*_*p76 15
更新:正如接受的答案所说,现在不可能了。但下面的代码处理了我们的问题一段时间。我应该补充一点,至少我们必须切换到使用基于 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');
}
Run Code Online (Sandbox Code Playgroud)
感谢此链接:https : //jakearchibald.com/2016/streams-ftw/
Gab*_*iel 11
使用 fetch:现在可以使用 Chrome >= 105
如何: https: //developer.chrome.com/articles/fetch-streaming-requests/
目前其他浏览器不支持(也许您阅读本文时会出现这种情况,请相应地编辑我的答案)
特征检测(来源)
const supportsRequestStreams = (() => {
let duplexAccessed = false;
const hasContentType = new Request('', {
body: new ReadableStream(),
method: 'POST',
get duplex() {
duplexAccessed = true;
return 'half';
},
}).headers.has('Content-Type');
return duplexAccessed && !hasContentType;
})();
Run Code Online (Sandbox Code Playgroud)
需要 HTTP >= 2
如果连接是 HTTP/1.x,则提取将被拒绝。
我不认为这是可能的.草案规定:
当涉及到请求进展时,它目前缺乏[ 与XHR相比 ]
(旧答案):Fetch API章节中
的第一个示例提供了有关如何:
如果您想逐步接收身体数据:
Run Code Online (Sandbox Code Playgroud)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构造函数反模式之外,您还可以看到这response.body是一个Stream,您可以使用Reader逐字节读取,并且可以为每个事件触发事件或执行任何操作(例如,记录进度).
但是,Streams规范似乎没有完全完成,我不知道这是否已经适用于任何fetch实现.
因为所有答案都不能解决问题。
出于实现的目的,您可以使用一些已知大小的小初始块来检测上传速度,并且可以使用内容长度/上传速度来计算上传时间。您可以将此时间用作估计。
| 归档时间: |
|
| 查看次数: |
40792 次 |
| 最近记录: |