MDN之Web API 接口参考(四)【Blob】

博客围绕一个不可变、原始数据的类文件对象展开,介绍了其构造函数可用于创建新对象,阐述了对象包含数据的大小和类型等属性,说明了返回指定范围数据的方法,还给出了构造函数用法、提取数据等方面的示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Blob 对象表示一个不可变、原始数据的类文件对象。Blob 表示的不一定是JavaScript原生格式的数据。File 接口基于Blob,继承了 blob 的功能并将其扩展使其支持用户系统上的文件。

要从其他非blob对象和数据构造一个Blob,请使用 Blob() 构造函数。要创建包含另一个blob数据的子集blob,请使用 slice()方法。要获取用户文件系统上的文件对应的Blob对象,请参阅 File文档。

接受Blob对象的API也被列在 File 文档中。

注意:slice()方法原本接受length作为第二个参数,以表示复制到新Blob 对象的字节数。如果设置的参数使start + length超出了源Blob对象的大小,那返回的则是从start到结尾的数据。

注意:slice() 方法在某些浏览器和版本上带有浏览器引擎前缀:比如 Firefox 12及更早版本的blob.mozSlice()Safari中的blob.webkitSlice()。 没有浏览器引擎前缀的老版本slice()方法有不同的语义,并且已过时。 Firefox 30 中取消了对blob.mozSlice()的支持。

构造函数

Blob(blobParts[, options])

返回一个新创建的 Blob 对象,其内容由参数中给定的数组串联组成。

属性

Blob.size 只读

Blob 对象中所包含数据的大小(字节)。

Blob.type 只读

一个字符串,表明该Blob对象所包含数据的MIME类型。如果类型未知,则该值为空字符串。

方法

Blob.slice([start,[ end ,[contentType]]])

返回一个新的 Blob对象,包含了源 Blob对象中指定范围内的数据。

示例

Blob 构造函数用法举例

Blob() 构造函数 允许用其它对象创建 Blob 对象。比如,用字符串构建一个 blob

var debug = {hello: "world"};
var blob = new Blob([JSON.stringify(debug, null, 2)],
  {type : 'application/json'});

BlobBuilder 接口提供了另外一种创建Blob对象的方式,但该方式现在已经废弃,不应继续使用:

var builder = new BlobBuilder(); 
var fileParts = ['<a id="a"><b id="b">hey!</b></a>'];
 builder.append(fileParts[0]); 
var myBlob =builder.getBlob('text/xml'); ```

示例:使用 Blob 创建一个指向类型化数组的URL

参考下面的代码:

var typedArray = GetTheTypedArraySomehow();
var blob = new Blob([typedArray], {type: "application/octet-binary"});// 传入一个合适的MIME类型
var url = URL.createObjectURL(blob);
// 会产生一个类似blob:d3958f5c-0777-0845-9dcf-2cb28783acaf 这样的URL字符串
// 你可以像使用一个普通URL那样使用它,比如用在img.src上。

示例:从 Blob 中提取数据

Blob中读取内容的唯一方法是使用 FileReader。以下代码将 Blob 的内容作为类型数组读取:

var reader = new FileReader();
reader.addEventListener("loadend", function() {
   // reader.result 包含转化为类型数组的blob
});
reader.readAsArrayBuffer(blob);

通过使用 FileReader 的其它方法可以把Blob读取为字符串或者数据URL

<think>我们正在实现一个基于FastAPI和Vue3的流式文件下载功能。流式下载适用于大文件,可以分块传输,避免服务器内存溢出,同时允许用户边下载边查看。 步骤: 1. 后端(FastAPI)设置一个支持流式响应的下载端点。 2. 前端(Vue3 + NaiveUI)使用axios或fetch进行请求,并处理流式数据,将数据流保存为文件。 后端实现(FastAPI): 我们将使用FastAPI的`StreamingResponse`来返回文件流。假设我们有一个大文件,我们不想一次性加载到内存中,而是以块的形式发送。 示例代码(FastAPI): ```python from fastapi import FastAPI from fastapi.responses import StreamingResponse import os app = FastAPI() @app.get("/download") async def download_file(): # 假设我们有一个大文件,路径为/path/to/large/file.zip file_path = "/path/to/large/file.zip" if not os.path.exists(file_path): return {"error": "File not found"} # 定义一个生成器函数,用于逐块读取文件 def iter_file(): with open(file_path, "rb") as f: while chunk := f.read(1024 * 1024): # 每次读取1MB yield chunk # 设置响应头,指定内容为附件,并设置文件名 headers = { "Content-Disposition": f"attachment; filename={os.path.basename(file_path)}" } return StreamingResponse(iter_file(), headers=headers) ``` 前端实现(Vue3 + NaiveUI): 在前端,我们使用`axios`发送请求,并设置`responseType`为`'blob'`。然后,我们将接收到的数据块转换为Blob对象,并利用URL.createObjectURL生成下载链接,最后通过动态创建a标签触发下载。 但是,由于我们使用的是流式传输,实际上axios会等待所有数据接收完毕才返回blob。对于大文件,我们可能希望边接收边保存,这可以使用Streams API(如果浏览器支持)来实现。然而,考虑到兼容性,这里我们使用两种方式: 1. 小文件:直接使用axios接收blob,然后下载。 2. 大文件:使用fetch和ReadableStream来边接收边写入。 由于用户要求流式下载,我们重点介绍大文件的处理方式(使用fetch和流式处理): 示例代码(Vue3组件): ```vue <template> <n-button @click="downloadFile">下载文件</n-button> </template> <script> import { defineComponent } from 'vue' import { useMessage } from 'naive-ui' export default defineComponent({ setup() { const message = useMessage() const downloadFile = async () => { try { const response = await fetch('http://your-api-domain/download') if (!response.ok) { throw new Error(`下载失败: ${response.statusText}`) } // 获取文件名(从Content-Disposition中解析) const contentDisposition = response.headers.get('Content-Disposition') let filename = 'downloaded_file' if (contentDisposition) { const match = contentDisposition.match(/filename="?(.+?)"?$/) if (match) filename = match[1] } // 使用Streams API处理流 const reader = response.body.getReader() const stream = new ReadableStream({ start(controller) { function push() { reader.read().then(({ done, value }) => { if (done) { controller.close() return } controller.enqueue(value) push() }) } push() } }) // 转换为Blob并下载 const newResponse = new Response(stream) const blob = await newResponse.blob() const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = filename document.body.appendChild(a) a.click() a.remove() URL.revokeObjectURL(url) } catch (error) { message.error(error.message) } } return { downloadFile } } }) </script> ``` 但是,上述代码中,我们虽然使用了流读取,但最终仍然是将整个文件加载到内存中形成Blob,对于超大文件可能会占用大量内存。为了真正实现流式下载(边下载边写入磁盘),我们可以使用FileSystemWritableFileStream(属于File System Access API),但该API目前只有Chrome等较新浏览器支持。 另一种方案是使用Service Worker和Cache API,但比较复杂。对于普通应用,如果文件不是特别大,上述方法已经足够。如果文件非常大,我们可以考虑使用分块下载并保存到临时文件,然后组合,但这在前端并不常见。 考虑到兼容性和简单性,我们可以使用以下方法(使用createObjectURL)对于不是特别大的文件(比如几个GB以内)是可以接受的,因为现代浏览器可以处理。但如果是超大文件,建议用户使用专门的下载工具,或者后端提供分片下载,前端分片下载并保存(需要IndexedDB等存储)。 由于时间限制,我们这里展示一个折中方案:使用Streams API将数据流写入到用户磁盘(使用showSaveFilePicker,需要浏览器支持)。这样就能边下载边保存,而不会占用太多内存。 修改后的前端代码(使用File System Access API): ```vue <template> <n-button @click="downloadFile">下载文件</n-button> </template> <script> import { defineComponent } from 'vue' import { useMessage } from 'naive-ui' export default defineComponent({ setup() { const message = useMessage() const downloadFile = async () => { try { const response = await fetch('http://your-api-domain/download') if (!response.ok) { throw new Error(`下载失败: ${response.statusText}`) } // 获取文件名 const contentDisposition = response.headers.get('Content-Disposition') let filename = 'downloaded_file' if (contentDisposition) { const match = contentDisposition.match(/filename="?(.+?)"?$/) if (match) filename = match[1] } // 请求用户选择保存位置 if (!window.showSaveFilePicker) { throw new Error('您的浏览器不支持文件系统访问API,请使用最新版Chrome或Edge') } const handle = await window.showSaveFilePicker({ suggestedName: filename }) const writable = await handle.createWritable() // 使用流式写入 const reader = response.body.getReader() while (true) { const { done, value } = await reader.read() if (done) break await writable.write(value) } await writable.close() message.success('文件下载完成') } catch (error) { message.error(error.message) } } return { downloadFile } } }) </script> ``` 注意:`showSaveFilePicker`目前只在安全上下文(HTTPS)和部分浏览器中可用。因此,在生产环境中,需要确保网站是HTTPS,并且对不支持的浏览器提供备选方案(比如使用传统的Blob方式)。 总结: 1. 后端使用FastAPI的StreamingResponse,以流的形式发送文件。 2. 前端使用fetch获取响应流,然后使用File System Access API将流直接写入用户选择的文件(边下载边保存),或者对于不支持此API的浏览器,使用Blob方式(但大文件可能会内存占用高)。 请根据实际需求选择合适的前端实现方式。 相关问题: 1. 如何在后端FastAPI中实现文件分块上传? 2. Vue3中如何处理下载进度显示? 3. 如何优化大文件下载的体验? 4. 在哪些场景下使用流式下载更有优势? 5. 如何保证文件下载的安全性(例如身份验证)?</think>下面我将详细介绍如何使用 FastAPI 后端和 Vue3 前端(搭配 NaiveUI)实现流式文件下载功能,这种方案特别适合大文件下载,避免内存溢出并提供更好的用户体验。 ### 一、后端实现(FastAPI) ```python from fastapi import FastAPI, HTTPException from fastapi.responses import StreamingResponse import os app = FastAPI() @app.get("/download/{file_name}") async def download_file(file_name: str): # 设置文件存储路径(根据实际情况修改) FILE_DIR = "/path/to/your/files" file_path = os.path.join(FILE_DIR, file_name) # 检查文件是否存在 if not os.path.exists(file_path): raise HTTPException(status_code=404, detail="文件不存在") # 流式响应实现 def file_iterator(): with open(file_path, "rb") as f: while chunk := f.read(1024 * 1024): # 每次读取1MB yield chunk # 设置响应头(关键) headers = { "Content-Disposition": f"attachment; filename={file_name}", "Content-Type": "application/octet-stream" } return StreamingResponse( file_iterator(), headers=headers, media_type="application/octet-stream" ) ``` **关键点说明**: 1. 使用 `StreamingResponse` 实现流式传输 2. 通过生成器函数 `file_iterator` 分块读取文件(每次1MB) 3. `Content-Disposition` 头确保浏览器触发下载 4. `application/octet-stream` MIME类型表示二进制流 ### 二、前端实现(Vue3 + NaiveUI) ```vue <template> <n-card title="文件下载"> <n-input v-model:value="filename" placeholder="输入文件名" /> <n-button type="primary" @click="downloadFile" :loading="downloading" class="mt-4" > 下载文件 </n-button> <!-- 下载进度显示 --> <n-progress v-if="downloading" type="line" :percentage="progress" :indicator-placement="'inside'" class="mt-4" /> </n-card> </template> <script setup> import { ref } from 'vue'; import { useMessage } from 'naive-ui'; const message = useMessage(); const filename = ref('large_file.zip'); const downloading = ref(false); const progress = ref(0); const downloadFile = async () => { try { downloading.value = true; progress.value = 0; const response = await fetch(`http://your-api-domain/download/${filename.value}`); if (!response.ok) { throw new Error(`下载失败: ${response.statusText}`); } // 获取文件大小(用于进度计算) const contentLength = response.headers.get('content-length'); const totalSize = contentLength ? parseInt(contentLength) : null; // 创建读取器 const reader = response.body.getReader(); let receivedSize = 0; let chunks = []; while (true) { const { done, value } = await reader.read(); if (done) break; chunks.push(value); receivedSize += value.length; // 更新进度(如果有文件大小信息) if (totalSize) { progress.value = Math.round((receivedSize / totalSize) * 100); } } // 合并数据块 const blob = new Blob(chunks); const url = URL.createObjectURL(blob); // 创建下载链接 const a = document.createElement('a'); a.href = url; a.download = filename.value; document.body.appendChild(a); a.click(); // 清理 URL.revokeObjectURL(url); document.body.removeChild(a); message.success('文件下载完成!'); } catch (error) { message.error(`下载失败: ${error.message}`); } finally { downloading.value = false; } }; </script> ``` ### 三、关键优化点 1. **大文件处理优化**: - 后端使用流式响应(`StreamingResponse`) - 前端使用 `ReadableStream` 分块处理 - 避免一次性加载大文件到内存 2. **进度显示**: - 通过 `Content-Length` 头获取文件总大小 - 计算已接收数据比例更新进度条 - 使用 NaiveUI 的 `<n-progress>` 组件 3. **错误处理**: - 文件不存在返回 404 - 网络错误捕获 - 使用 NaiveUI 的消息提示 4. **安全增强**: ```python # 在后端添加安全验证 @app.get("/download/{file_name}") async def download_file( file_name: str, current_user: User = Depends(authenticate) ): # 验证用户权限... ``` ### 、部署注意事项 1. **跨域问题**(开发环境): ```python # 在FastAPI中添加CORS中间件 from fastapi.middleware.cors import CORSMiddleware app.add_middleware( CORSMiddleware, allow_origins=["http://localhost:3000"], allow_methods=["*"], allow_headers=["*"], ) ``` 2. **生产环境配置**: - 使用 Nginx 反向代理 - 启用 HTTPS - 设置合理的超时时间: ```nginx proxy_read_timeout 300s; proxy_send_timeout 300s; ``` ### 五、相关技术文档参考 [^1]: FastAPI 官方文档 - StreamingResponse 使用 [^2]: MDN Web Docs - ReadableStream API [^3]: NaiveUI 进度条组件文档
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值