要使用 fetch
API 下载文件并监控下载进度,你需要处理响应的 ReadableStream
。这里是一个如何封装此功能的基本示例:
<script setup lang="ts">
const downloadFileWithProgress = async (url, onProgress) => {// 发起 fetch 请求const response = await fetch(url);if (!response.ok) {throw new Error(`HTTP error! status: ${response.status}`);}const reader = response.body.getReader();const contentLength = +response.headers.get('Content-Length');let receivedLength = 0; // 接收到的字节数let chunks = []; // 用于存储二进制块的数组// 读取数据while (true) {const { done, value } = await reader.read();if (done) {break;}chunks.push(value);receivedLength += value.length;// 调用 onProgress 回调函数if (onProgress) {onProgress(receivedLength / contentLength);}}// 将所有块合并成一个 Uint8Arraylet chunksAll = new Uint8Array(receivedLength);let position = 0;for (let chunk of chunks) {chunksAll.set(chunk, position);position += chunk.length;}// 创建一个 Blob 并返回 URLconst blob = new Blob([chunksAll]);const downloadUrl = URL.createObjectURL(blob);return downloadUrl;
};// 这个例子中的 onProgress 函数只是打印出下载进度
const onProgress = (progress) => {console.log(`Downloaded ${Math.round(progress * 100)}%`);
};// 调用封装的函数
const downloadUrl = 'https://example.com/file'; // 设置你的文件 URLconst startDownload = async () => {try {const url = await downloadFileWithProgress(downloadUrl, onProgress);// 创建一个a标签来触发下载const a = document.createElement('a');a.href = url;a.download = 'file_name.ext'; // 设置下载文件的名称document.body.appendChild(a);a.click();document.body.removeChild(a);URL.revokeObjectURL(url);} catch (error) {console.error('Download failed:', error);}
};
</script><template><button @click="startDownload">Download File</button>
</template>
在这个示例中,
downloadFileWithProgress
函数接受一个文件 URL 和一个进度回调函数。使用fetch
请求文件,然后通过读取ReadableStream
来处理文件流。当读取到新的数据块时,进度回调会被调用,更新下载进度。读取完成后,它将所有数据块合并到一个Uint8Array
中,然后创建一个Blob
,最终生成一个可以下载的 URL。请注意,进度监控假设服务器发送了正确的
Content-Length
头。如果没有这个头,或者它的值不正确,那么进度指示将无法工作。在实际应用中,确保处理各种可能的异常情况,并提供用户反馈。此外,由于 CORS 策略,你可能无法从其他域获取
Content-Length
头,除非其他域的服务器设置了适当的 CORS 头来允许这样做。