Deno Oak:如何在单个响应中向浏览器发送多Blob对象
解决方案
方案一:修复FormData响应的Content-Type与序列化问题
Oak直接返回FormData对象时,不会自动处理multipart/form-data的boundary和正确序列化逻辑,需要手动将FormData转为Blob并设置响应头:
后端(Oak)代码
import { Application, Router } from "https://deno.land/x/oak@v12.6.1/mod.ts"; const router = new Router(); router.post("/myroute/get", async (context) => { // 构建FormData,包含文本和Blob示例 const formData = new FormData(); formData.append("key1", "value1"); formData.append("key2", "value2"); // 模拟图片Blob(替换为你的实际文件路径) const imageBuffer = await Deno.readFile("./test-image.jpg"); const imageBlob = new Blob([imageBuffer], { type: "image/jpeg" }); formData.append("image1", imageBlob, "test.jpg"); // 将FormData序列化为Blob,Deno会自动生成boundary const formBlob = new Blob([formData], { type: "multipart/form-data" }); const contentType = formBlob.type; // 包含自动生成的boundary // 设置响应头与响应体 context.response.headers.set("Content-Type", contentType); context.response.body = formBlob; }); const app = new Application(); app.use(router.routes()); app.use(router.allowedMethods()); await app.listen({ port: 8000 });
客户端代码
async function fetchFormData() { const myFormData = new FormData(); myFormData.append("clientKey", "clientValue"); try { const res = await fetch(`http://localhost:8000/myroute/get`, { method: 'POST', body: myFormData, }); const formData = await res.formData(); // 读取响应内容 console.log("key1:", formData.get("key1")); console.log("key2:", formData.get("key2")); const imageBlob = formData.get("image1"); console.log("图片Blob:", imageBlob); // 可将Blob转为URL用于显示:document.getElementById('img').src = URL.createObjectURL(imageBlob); } catch (err) { console.error("请求失败:", err); } } fetchFormData();
方案二:用Zip打包多个Blob(替代FormData)
如果不需要键值对结构,将多个Blob打包为Zip返回也是高效的方案,客户端下载后解压读取:
后端(Oak + Deno Zip API)代码
import { Application, Router } from "https://deno.land/x/oak@v12.6.1/mod.ts"; const router = new Router(); router.post("/myroute/get-zip", async (context) => { // 创建Zip写入器 const zipWriter = new Deno.ZipWriter(new Blob()); // 添加文本内容 await zipWriter.add("key1.txt", { data: "value1" }); await zipWriter.add("key2.txt", { data: "value2" }); // 添加图片文件 const imageData = await Deno.readFile("./test-image.jpg"); await zipWriter.add("test.jpg", { data: imageData }); // 完成Zip打包并转为Blob const zipBlob = await zipWriter.close(); // 设置响应头,标记为Zip文件 context.response.headers.set("Content-Type", "application/zip"); context.response.headers.set("Content-Disposition", "attachment; filename=\"assets.zip\""); context.response.body = zipBlob; }); const app = new Application(); app.use(router.routes()); app.use(router.allowedMethods()); await app.listen({ port: 8000 });
客户端代码(使用JSZip解压)
// 引入JSZip库 import JSZip from "https://cdn.jsdelivr.net/npm/jszip@3.10.1/dist/jszip.min.js"; async function fetchAndExtractZip() { const myFormData = new FormData(); myFormData.append("clientKey", "clientValue"); try { const res = await fetch(`http://localhost:8000/myroute/get-zip`, { method: 'POST', body: myFormData, }); const zipBlob = await res.blob(); const zip = await JSZip.loadAsync(zipBlob); // 读取解压后的内容 const key1 = await zip.file("key1.txt")?.async("text"); const key2 = await zip.file("key2.txt")?.async("text"); const imageBlob = await zip.file("test.jpg")?.async("blob"); console.log("key1:", key1); console.log("key2:", key2); console.log("图片Blob:", imageBlob); } catch (err) { console.error("Zip处理失败:", err); } } fetchAndExtractZip();
内容的提问来源于stack exchange,提问作者Imagitori
相关产品推荐
相关产品推荐

