如何使用 GraphQL 将二进制数据发送回客户端

问题描述 投票:0回答:5

我有一个 GraphQL 服务器,托管在 Express 上。我想通过发送回 NodeJS 缓冲区对象来将图像返回给客户端。我如何配置 graphql 服务器以返回字节而不是 json?我不想通过 base64 来执行此操作,因为图像尺寸很大。

javascript node.js graphql
5个回答
7
投票

你必须返回JSON,但还有办法。 我们使用 GraphQL 返回存储在旧版 SQL 数据库中的 Blob 字段中的图像。 我们使用sequelize、graphql-sequelize 和graphql-js。 我们已经在 graphql 模式中将 Blob 字段定义为 String 类型,因此它们可以很好地通过 json 回复。 然后我们在传递之前转换为缓冲区,就像

const imgBuffer = new Buffer.from(imgObj.data, 'ascii');

唯一的问题是我们现在无法通过 graphql 接口将图像数据保存回数据库。 当我们的突变函数在字符串中发现某些错误的 unicode 字符时,例如 \U0000 之类的,就会给我们带来语法错误(所以我发现你的问题正在寻找解决方案)。


1
投票

有一种方法,但它很复杂,而且非常手动,我只会向您概述我在 ApolloServer 中所做的事情,但我认为这应该足够了。

首先,您需要在请求中使用“Accept”标头发送二进制 mime 类型,并在响应中发送匹配的“Content-Type”。正如您将看到的(使用 EJSON),这对于提高效率是必要的,但对于工作却不是必要的。

要序列化和反序列化尊重标头,您可能需要编写一个快速中间件,并且您需要使用 {$data: "..."} 封装对象(如 EJSON 那样)或只是(奇怪的是)处理 base64 编码如果有人使用“application/json”作为“accept”标头发出二进制数据请求,则返回 null。您还需要选择支持的二进制格式。我只使用1:“application/x-msgpack”,但我听说“application/cbor”变得越来越流行。您可以使用 EJSON、MessagePack 和 CBOR 库来进行序列化,因此这并不像听起来那么难。

我强烈建议在任何图像上使用@defer。有关 @defer 的更多信息,请参阅这篇文章:https://www.apollographql.com/blog/introducing-defer-in-apollo-server-f6797c4e9d6e/

我已经做到了。这并不容易,如果 ApolloServer 能够“开箱即用”地工作那就更好了。


1
投票

最好发送散列临时链接来下载

  • 可以对 URL 进行哈希处理,使其无法被其他用户访问。
  • 后端可以使链接失效或删除静态服务器上的二进制文件。

0
投票

The Guild 提供了一个自定义的 Byte scalar 来处理这个问题。

例如,假设您想将 CSV 缓冲区传递给客户端,然后下载它:

在您的 GraphQl 架构中:

scalar Byte

type Query {
  myFile: MyFile!
}

type MyFile {
  filename: String!
  contentType: String!
  byteBuffer: Byte!
}

在您的服务器上发送:

// create the csv string by hand or with your favorite library
return {
  filename: 'my.csv',
  contentType: 'text/csv',
  byteBuffer: Buffer.from(csv)
};

在您的客户端上,接收它:

// setup your query then call it
const result = await myFile();
const { filename, contentType, byteBuffer } = result.data.myFile;
const blob = createBlobFromByteBuffer(byteBuffer, contentType);
downloadFile(blob, filename);

在您的客户端上,有这些实用程序:

export const createBlobFromByteBuffer = (byteBuffer: { data: any }, contentType: string): Blob => {
  return new Blob([new Uint8Array(byteBuffer.data)], { type: contentType });
};

export const downloadFile = (blob: Blob, filename: string): void => {
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  a.click();
  a.remove();
  URL.revokeObjectURL(url);
};

-1
投票

使用here找到的节点模块可能会回答您的问题。

© www.soinside.com 2019 - 2024. All rights reserved.