如何使用GraphQL将图像上传到AWS S3?

13

我正在上传一个Base64字符串,但GraphQL卡住了。如果我将字符串切片为少于50,000个字符,则可以正常工作。超过50,000个字符后,GraphQL永远无法到达解析函数,但不会报错。对于较小的字符串,它可以正常工作。

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 },
  },
});
2个回答

7
这里正确的方法是使用AWS AppSync通过复杂类型执行实际的S3上传-您在这里所示的看起来更像是尝试将base64编码的图像保存为字符串到我只能假设为DynamoDB表条目的字段中。但要使此操作起作用,您需要修改变更,使file字段不是String!而是S3ObjectInput
在此“正常工作”(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对象元数据。可以通过以下方式在mutation的请求解析器中处理它:

{
    "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我们想要将模型的file字段中存储的JSON字符串解析为一个S3Object。这样,在查询模型时,不会返回存储在file字段中的字符串,而是会得到一个包含bucketregionkey属性的对象,您可以使用它们构建URL来访问S3 Object(可以直接通过S3或使用CDN访问,这实际上取决于您的配置)。
确保已经设置了复杂对象的凭证,然而(我告诉你我会回到这个问题)。我将使用React示例来说明 - 在定义AppSync参数(端点,身份验证等)时,还需要定义一个名为complexObjectCredentials的额外属性来告诉客户端使用哪些AWS凭证来处理S3上传,例如:
const client = new AWSAppSyncClient({
    url: AppSync.graphqlEndpoint,
    region: AppSync.region,
    auth: {
        type: AUTH_TYPE.AWS_IAM,
        credentials: () => Auth.currentCredentials()
    },
    complexObjectsCredentials: () => Auth.currentCredentials(),
});

假设这些条件都已经就位,通过 AppSync 进行的 S3 上传和下载应该可以正常工作。

4
AWS AppSync(https://aws.amazon.com/appsync/)提供称为“复杂对象”的功能,其中您可以拥有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

谢谢@Richard。您引用的教程没有展示如何更改NewPostMutation.js文件。我尝试按照这个教程,但到目前为止还没有成功上传文件到s3 :-( - SaidAkh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接