Cloudflare Hono API从KV获取压缩数据返回为空问题排查
问题:Cloudflare Hono从KV获取分块压缩数据返回为空
我正在基于Cloudflare Hono搭建API站点,需要从Cloudflare KV中获取ZLIB格式的项目。这些项目以<key_name>_chunk_0、<key_name>_chunk_1等分块形式存储。我理想的需求是返回压缩对象列表,退而求其次也可以返回单个压缩对象(将在客户端进行解压)。但目前客户端收到的返回内容为空,我已确认KV存储的命名空间和访问权限均无问题。
简化后的代码:
import { Hono } from 'hono'; const app = new Hono(); async function fetchFromKV(env, baseKey) { const chunks = []; let chunkIndex = 0; while (true) { const chunkKey = `${baseKey}_chunk_${chunkIndex}`; const chunkData = await env.KEYNAMESPACE.get(chunkKey, "arrayBuffer"); if (chunkData === null) { // No more chunks found, break the loop break; } chunks.push(chunkData); chunkIndex++; } return chunks; } app.get('/api/getHello', async (c) => { try { const data = await fetchFromKV(c.env, 'key-for-zipped-item'); // Use await to fetch data return c.json({ message: 'Here is your data!', data: data }); } catch (e) { return c.json({ error: e.message }, 500); } }); export default app;
编辑:我也尝试了获取GZIP格式的项目,同样未能成功:
app.get('/api/getGzipItem', async (c) => { // Basic auth check (you may want to adjust this based on your actual auth setup) const key = '<key_name>_chunk_0'; const gzipData = await c.env.RESSADA.get(key, "arrayBuffer"); if (gzipData === null) { return c.json({ message: 'Item not found' }, 404); } c.header('Content-Type', 'application/gzip'); c.header('Content-Encoding', 'gzip'); // Return the compressed data as a raw response return c.body(gzipData); });
问题分析与修复方案
1. JSON序列化无法处理ArrayBuffer
第一个接口/api/getHello中,你直接将ArrayBuffer数组放入c.json()的data字段,但JSON不支持序列化ArrayBuffer类型,会将其转为空对象,导致客户端收到空数据。
修复方法:将ArrayBuffer转为Base64字符串传输,客户端再转回ArrayBuffer解压:
async function fetchFromKV(env, baseKey) { const chunks = []; let chunkIndex = 0; while (true) { const chunkKey = `${baseKey}_chunk_${chunkIndex}`; const chunkData = await env.KEYNAMESPACE.get(chunkKey, "arrayBuffer"); if (chunkData === null) { break; } // 将ArrayBuffer转为Base64字符串 chunks.push(btoa(String.fromCharCode(...new Uint8Array(chunkData)))); chunkIndex++; } return chunks; } app.get('/api/getHello', async (c) => { try { const data = await fetchFromKV(c.env, 'key-for-zipped-item'); return c.json({ message: 'Here is your data!', data: data }); } catch (e) { return c.json({ error: e.message }, 500); } });
2. GZIP接口的两个潜在问题
- 命名空间不一致:第一个接口用
KEYNAMESPACE,第二个用RESSADA,需确保和你绑定的KV命名空间一致; - 二进制数据返回方式:Hono的
c.body()处理ArrayBuffer时可能存在兼容性问题,建议直接用Response对象返回。
修复后的GZIP接口:
app.get('/api/getGzipItem', async (c) => { const key = '<key_name>_chunk_0'; // 统一命名空间为KEYNAMESPACE,和第一个接口保持一致 const gzipData = await c.env.KEYNAMESPACE.get(key, "arrayBuffer"); if (gzipData === null) { return c.json({ message: 'Item not found' }, 404); } return new Response(gzipData, { headers: { 'Content-Type': 'application/gzip', 'Content-Encoding': 'gzip' } }); });
3. 补充:合并分块返回完整压缩文件
如果需要将所有分块合并为一个完整压缩文件返回,可修改代码合并ArrayBuffer:
async function fetchAndMergeChunks(env, baseKey) { let totalLength = 0; const chunks = []; let chunkIndex = 0; // 先获取所有分块并计算总长度 while (true) { const chunkKey = `${baseKey}_chunk_${chunkIndex}`; const chunkData = await env.KEYNAMESPACE.get(chunkKey, "arrayBuffer"); if (chunkData === null) { break; } chunks.push(chunkData); totalLength += chunkData.byteLength; chunkIndex++; } // 合并所有分块到一个ArrayBuffer const mergedBuffer = new Uint8Array(totalLength); let offset = 0; for (const chunk of chunks) { mergedBuffer.set(new Uint8Array(chunk), offset); offset += chunk.byteLength; } return mergedBuffer.buffer; } // 调用示例 app.get('/api/getFullZlib', async (c) => { try { const data = await fetchAndMergeChunks(c.env, 'key-for-zipped-item'); return new Response(data, { headers: { 'Content-Type': 'application/zlib', 'Content-Encoding': 'deflate' } }); } catch (e) { return c.json({ error: e.message }, 500); } });
内容的提问来源于stack exchange,提问作者user791793
相关产品推荐
相关产品推荐

