加载笔记内容...
加载笔记内容...
https://stackoverflow.com/questions/35711724/upload-progress-indicators-for-fetch
Demo: File Upload Progress Demo - Using Fetch Duplex Streaming
1/**
2 * 表示上传进度事件的类
3 */
4class FetchProgressEvent {
5 constructor(
6 public lengthComputable: boolean,
7 public loaded: number,
8 public total: number,
9 ) {}
10}
11
12/**
13 * 计算流的总大小
14 */
15export async function getStreamSize(stream: ReadableStream): Promise<number> {
16 const reader = stream.getReader()
17 let size = 0
18
19 while (true) {
20 const { done, value } = await reader.read()
21 if (done)
22 break
23 size += value.length
24 }
25
26 return size
27}
28
29/**
30 * 为请求添加进度跟踪功能
31 * @param request 原始请求
32 * @param onProgress 进度回调
33 */
34async function trackRequestProgress(
35 request: Request,
36 onProgress?: (evt: FetchProgressEvent) => void,
37): Promise<Request> {
38 if (!onProgress)
39 return request
40
41 // 检查浏览器支持
42 const supportsStreaming = (() => {
43 let isDuplex = false
44 const test = new Request(request.url, {
45 body: new ReadableStream(),
46 method: 'POST',
47 get duplex() {
48 isDuplex = true
49 return 'half' as const
50 },
51 })
52 return isDuplex && !test.headers.has('Content-Type')
53 })()
54
55 if (!request.body || !supportsStreaming)
56 return request
57
58 const [stream1, stream2] = request.body.tee()
59 const totalSize = await getStreamSize(stream1)
60 let loaded = 0
61
62 const progress = new TransformStream({
63 start: () => {
64 onProgress(new FetchProgressEvent(true, 0, totalSize))
65 },
66 transform: (chunk, controller) => {
67 controller.enqueue(chunk)
68 loaded += chunk.byteLength
69 onProgress(new FetchProgressEvent(true, loaded, totalSize))
70 },
71 flush: () => {
72 onProgress(new FetchProgressEvent(true, totalSize, totalSize))
73 },
74 })
75
76 return new Request(request.url, {
77 method: 'POST',
78 headers: request.headers,
79 body: stream2.pipeThrough(progress),
80 duplex: 'half',
81 })
82}
83
84export {
85 FetchProgressEvent,
86 trackRequestProgress,
87}
XMLHttpRequest
或 axios
如果需要更简单的方式监控 FormData
的上传进度,可以直接使用 XMLHttpRequest
或 axios
。以下是 axios
的实现:
1function uploadWithProgress(url, file) {
2 const formData = new FormData();
3 formData.append('file', file);
4
5 axios.post(url, formData, {
6 headers: {
7 'Content-Type': 'multipart/form-data',
8 },
9 onUploadProgress: (progressEvent) => {
10 const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
11 console.log(`Upload Progress: ${percentCompleted}%`);
12 },
13 })
14 .then(response => {
15 console.log('Upload completed successfully!', response.data);
16 })
17 .catch(error => {
18 console.error('Upload failed:', error);
19 });
20}
21
22const fileInput = document.querySelector('#fileInput');
23fileInput.addEventListener('change', () => {
24 const file = fileInput.files[0];
25 if (file) {
26 uploadWithProgress('https://example.com/upload', file);
27 }
28});
避免了手动处理流和进度的复杂逻辑,更加简洁易用,适用于大多数场景。