在TypeScript Deno中将ReadableStream<Uint8Array>转为Uint8ClampedArray
解决方案
首先要明确:你通过fetch获取到的ReadableStream<Uint8Array>是图片文件的原始二进制数据(比如JPG/PNG格式的字节),并非BlurHash所需的像素数组(RGBA格式,长度为宽×高×4)。直接将其传入BlurHash会因尺寸不匹配报错,必须先解码图片得到像素数据,再转换为Uint8ClampedArray。
由于Deno无浏览器document环境,无法使用原生Canvas,可通过以下两种方式解决:
方法一:使用Deno第三方Canvas库(通用方案)
借助deno.land/x/canvas实现类似浏览器的图片解码与像素提取,步骤如下:
1. 实现流转Uint8Array工具函数
先将ReadableStream合并为完整的图片二进制数组:
async function streamToUint8Array(stream: ReadableStream<Uint8Array>): Promise<Uint8Array> { const reader = stream.getReader(); const chunks: Uint8Array[] = []; let result; while (!(result = await reader.read()).done) { chunks.push(result.value); } // 合并所有分片 const totalLength = chunks.reduce((acc, chunk) => acc + chunk.length, 0); const merged = new Uint8Array(totalLength); let offset = 0; for (const chunk of chunks) { merged.set(chunk, offset); offset += chunk.length; } return merged; }
2. 解码图片并获取像素数据
通过Canvas库加载图片,提取RGBA格式的Uint8ClampedArray:
import { createCanvas, loadImage } from "https://deno.land/x/canvas@v1.4.1/mod.ts"; async function getImagePixels(stream: ReadableStream<Uint8Array>): Promise<{ width: number; height: number; data: Uint8ClampedArray; }> { const imageBuffer = await streamToUint8Array(stream); const image = await loadImage(imageBuffer); const canvas = createCanvas(image.width, image.height); const ctx = canvas.getContext("2d"); if (!ctx) throw new Error("无法获取Canvas上下文"); ctx.drawImage(image, 0, 0); const imageData = ctx.getImageData(0, 0, image.width, image.height); return { width: image.width, height: image.height, data: imageData.data // 这就是所需的Uint8ClampedArray }; }
3. 生成BlurHash
将获取到的像素数据传入BlurHash编码函数:
import { encode } from "https://deno.land/x/blurhash@v1.1.2/mod.ts"; // 假设你已获取到图片流 const imageStream = (await fetch("你的图片URL")).body!; const { width, height, data } = await getImagePixels(imageStream); const blurhash = encode(data, width, height, 4, 4); // 最后两个参数为组件数量,可按需调整
方法二:针对特定图片格式解码(轻量方案)
如果仅处理单一格式(如JPG),可使用专用解码库,避免引入Canvas依赖。以JPG为例:
import { decode } from "https://deno.land/x/jpeg_decoder@v0.1.2/mod.ts"; import { streamToUint8Array } from "./你的工具文件.ts"; async function getJpegPixels(stream: ReadableStream<Uint8Array>): Promise<{ width: number; height: number; data: Uint8ClampedArray; }> { const buffer = await streamToUint8Array(stream); const decoded = decode(buffer); // 将JPG的RGB数据转换为RGBA格式(添加Alpha通道,默认不透明) const rgbaData = new Uint8ClampedArray(decoded.width * decoded.height * 4); for (let i = 0; i < decoded.data.length; i += 3) { const rgbaIndex = (i / 3) * 4; rgbaData[rgbaIndex] = decoded.data[i]; // R rgbaData[rgbaIndex + 1] = decoded.data[i + 1]; // G rgbaData[rgbaIndex + 2] = decoded.data[i + 2]; // B rgbaData[rgbaIndex + 3] = 255; // Alpha(不透明) } return { width: decoded.width, height: decoded.height, data: rgbaData }; }
关于Uint8Array转Uint8ClampedArray
如果你的Uint8Array已经是像素数据(而非图片二进制),可直接转换:
const uint8Array = new Uint8Array([255, 0, 0, 255]); const uint8Clamped = new Uint8ClampedArray(uint8Array);
但此操作仅适用于已解码的像素数组,不能直接用于图片文件的二进制数据。
内容的提问来源于stack exchange,提问作者progNewbie
相关产品推荐
相关产品推荐

