You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 14:37:10