如何使用 GraphQL 将图像上传到 AWS S3?
How to upload an image to AWS S3 using GraphQL?
我正在上传 base64 字符串,但 GraphQL 挂起。如果我将字符串切成少于 50,000 个字符,它就可以工作。在 50,000 个字符之后,graphQL 从未进入 resolve 函数,但也没有给出错误。在较小的字符串上,它工作得很好。
const file = e.target.files[0];
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = () => {
const imageArray = reader.result;
this.context.fetch('/graphql', {
body: JSON.stringify({
query: `mutation s3Upload($img: String!) {
s3Upload(file: $img) {
logo,
}
}`,
variables: {
img: imageArray,
},
}),
}).then(response => response.json())
.then(({ data }) => {
console.log(data);
});
}
const s3Upload = {
type: S3Type,
args: {
file: { type: new NonNull(StringType) },
},
resolve: (root, args, { user }) => upload(root, args, user),
};
const S3Type = new ObjectType({
name: 'S3',
fields: {
logo: { type: StringType },
},
});
AWS AppSync (https://aws.amazon.com/appsync/) 提供了称为 "Complex Objects" 的功能,您可以在其中为 S3 对象和输入提供类型:
type S3Object {
bucket: String!
key: String!
region: String!
}
input S3ObjectInput {
bucket: String!
key: String!
region: String!
localUri: String
mimeType: String
}
然后您可以像这样将此对象定义为另一种类型的一部分:
type UserProfile {
id: ID!
name: String
file: S3Object
}
然后指定一个突变来添加它:
type Mutation {
addUser(id: ID! name: String file: S3ObjectInput): UserProfile!
}
您的客户端操作需要指定适当的存储桶、密钥(带文件扩展名)、区域等。
更多信息:https://docs.aws.amazon.com/appsync/latest/devguide/building-a-client-app-react.html#complex-objects
此处正确的方法是使用 AWS AppSync 通过复杂类型执行实际的 S3 上传 - 你在这里说明的内容看起来更像是你试图将 base64 编码的图像作为字符串保存到我能做的字段中只假设是 DynamoDB table 条目。但是,要使其起作用,您需要修改您的突变,使 file
字段不是 String!
,而是 S3ObjectInput
.
引擎盖下有一些活动部件,您需要确保在此 "just works" (TM) 之前就位。首先,您需要确保为 GraphQL 架构中定义的 S3 对象提供适当的输入和类型
enum Visibility {
public
private
}
input S3ObjectInput {
bucket: String!
region: String!
localUri: String
visibility: Visibility
key: String
mimeType: String
}
type S3Object {
bucket: String!
region: String!
key: String!
}
当然,S3ObjectInput
类型用于上传新文件 - 通过创建或更新嵌入了所述 S3 对象元数据的模型。它可以通过以下方式在突变的请求解析器中处理:
{
"version": "2017-02-28",
"operation": "PutItem",
"key": {
"id": $util.dynamodb.toDynamoDBJson($ctx.args.input.id),
},
#set( $attribs = $util.dynamodb.toMapValues($ctx.args.input) )
#set( $file = $ctx.args.input.file )
#set( $attribs.file = $util.dynamodb.toS3Object($file.key, $file.bucket, $file.region, $file.version) )
"attributeValues": $util.toJson($attribs)
}
这是假设 S3 文件对象是附加到 DynamoDB 数据源的模型的子字段。请注意,对 $utils.dynamodb.toS3Object()
的调用会设置复杂的 S3 对象 file
,它是模型的一个字段,类型为 S3ObjectInput
。以这种方式设置请求解析器处理文件到 S3 的上传(当所有凭据设置正确时 - 我们稍后会谈到),但它没有解决如何获取 S3Object
回来。这是附加到本地数据源的字段级解析器变得必要的地方。本质上,您需要在 AppSync 中创建一个本地数据源,并使用以下请求和响应解析器将其连接到架构中模型的 file
字段:
## Request Resolver ##
{
"version": "2017-02-28",
"payload": {}
}
## Response Resolver ##
$util.toJson($util.dynamodb.fromS3ObjectJson($context.source.file))
这个解析器只是告诉 AppSync 我们想要获取存储在 DynamoDB 中用于模型的 file
字段的 JSON 字符串并将其解析为 S3Object
- 这个这样,当您查询模型时,您不会返回存储在 file
字段中的字符串,而是会得到一个包含 bucket
、region
和 key
的对象可用于构建 URL 以访问 S3 对象的属性(直接通过 S3 或使用 CDN - 这实际上取决于您的配置)。
但是请确保您已为复杂对象设置了凭据(告诉过您我会回过头来)。我将使用一个 React 示例来说明这一点 - 在定义 AppSync 参数(端点、身份验证等)时,需要定义一个名为 complexObjectCredentials
的附加 属性 来告诉客户端什么用于处理 S3 上传的 AWS 凭证,例如:
const client = new AWSAppSyncClient({
url: AppSync.graphqlEndpoint,
region: AppSync.region,
auth: {
type: AUTH_TYPE.AWS_IAM,
credentials: () => Auth.currentCredentials()
},
complexObjectsCredentials: () => Auth.currentCredentials(),
});
假设所有这些都准备就绪,通过 AppSync 的 S3 上传和下载应该可以工作。
我正在上传 base64 字符串,但 GraphQL 挂起。如果我将字符串切成少于 50,000 个字符,它就可以工作。在 50,000 个字符之后,graphQL 从未进入 resolve 函数,但也没有给出错误。在较小的字符串上,它工作得很好。
const file = e.target.files[0];
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = () => {
const imageArray = reader.result;
this.context.fetch('/graphql', {
body: JSON.stringify({
query: `mutation s3Upload($img: String!) {
s3Upload(file: $img) {
logo,
}
}`,
variables: {
img: imageArray,
},
}),
}).then(response => response.json())
.then(({ data }) => {
console.log(data);
});
}
const s3Upload = {
type: S3Type,
args: {
file: { type: new NonNull(StringType) },
},
resolve: (root, args, { user }) => upload(root, args, user),
};
const S3Type = new ObjectType({
name: 'S3',
fields: {
logo: { type: StringType },
},
});
AWS AppSync (https://aws.amazon.com/appsync/) 提供了称为 "Complex Objects" 的功能,您可以在其中为 S3 对象和输入提供类型:
type S3Object {
bucket: String!
key: String!
region: String!
}
input S3ObjectInput {
bucket: String!
key: String!
region: String!
localUri: String
mimeType: String
}
然后您可以像这样将此对象定义为另一种类型的一部分:
type UserProfile {
id: ID!
name: String
file: S3Object
}
然后指定一个突变来添加它:
type Mutation {
addUser(id: ID! name: String file: S3ObjectInput): UserProfile!
}
您的客户端操作需要指定适当的存储桶、密钥(带文件扩展名)、区域等。
更多信息:https://docs.aws.amazon.com/appsync/latest/devguide/building-a-client-app-react.html#complex-objects
此处正确的方法是使用 AWS AppSync 通过复杂类型执行实际的 S3 上传 - 你在这里说明的内容看起来更像是你试图将 base64 编码的图像作为字符串保存到我能做的字段中只假设是 DynamoDB table 条目。但是,要使其起作用,您需要修改您的突变,使 file
字段不是 String!
,而是 S3ObjectInput
.
引擎盖下有一些活动部件,您需要确保在此 "just works" (TM) 之前就位。首先,您需要确保为 GraphQL 架构中定义的 S3 对象提供适当的输入和类型
enum Visibility {
public
private
}
input S3ObjectInput {
bucket: String!
region: String!
localUri: String
visibility: Visibility
key: String
mimeType: String
}
type S3Object {
bucket: String!
region: String!
key: String!
}
当然,S3ObjectInput
类型用于上传新文件 - 通过创建或更新嵌入了所述 S3 对象元数据的模型。它可以通过以下方式在突变的请求解析器中处理:
{
"version": "2017-02-28",
"operation": "PutItem",
"key": {
"id": $util.dynamodb.toDynamoDBJson($ctx.args.input.id),
},
#set( $attribs = $util.dynamodb.toMapValues($ctx.args.input) )
#set( $file = $ctx.args.input.file )
#set( $attribs.file = $util.dynamodb.toS3Object($file.key, $file.bucket, $file.region, $file.version) )
"attributeValues": $util.toJson($attribs)
}
这是假设 S3 文件对象是附加到 DynamoDB 数据源的模型的子字段。请注意,对 $utils.dynamodb.toS3Object()
的调用会设置复杂的 S3 对象 file
,它是模型的一个字段,类型为 S3ObjectInput
。以这种方式设置请求解析器处理文件到 S3 的上传(当所有凭据设置正确时 - 我们稍后会谈到),但它没有解决如何获取 S3Object
回来。这是附加到本地数据源的字段级解析器变得必要的地方。本质上,您需要在 AppSync 中创建一个本地数据源,并使用以下请求和响应解析器将其连接到架构中模型的 file
字段:
## Request Resolver ##
{
"version": "2017-02-28",
"payload": {}
}
## Response Resolver ##
$util.toJson($util.dynamodb.fromS3ObjectJson($context.source.file))
这个解析器只是告诉 AppSync 我们想要获取存储在 DynamoDB 中用于模型的 file
字段的 JSON 字符串并将其解析为 S3Object
- 这个这样,当您查询模型时,您不会返回存储在 file
字段中的字符串,而是会得到一个包含 bucket
、region
和 key
的对象可用于构建 URL 以访问 S3 对象的属性(直接通过 S3 或使用 CDN - 这实际上取决于您的配置)。
但是请确保您已为复杂对象设置了凭据(告诉过您我会回过头来)。我将使用一个 React 示例来说明这一点 - 在定义 AppSync 参数(端点、身份验证等)时,需要定义一个名为 complexObjectCredentials
的附加 属性 来告诉客户端什么用于处理 S3 上传的 AWS 凭证,例如:
const client = new AWSAppSyncClient({
url: AppSync.graphqlEndpoint,
region: AppSync.region,
auth: {
type: AUTH_TYPE.AWS_IAM,
credentials: () => Auth.currentCredentials()
},
complexObjectsCredentials: () => Auth.currentCredentials(),
});
假设所有这些都准备就绪,通过 AppSync 的 S3 上传和下载应该可以工作。