如何使用 AWS AppSync 将文件上传到 AWS S3

How to upload file to AWS S3 using AWS AppSync

在 AWS AppSync 文档中关注 this docs/tutorial

它指出:

With AWS AppSync you can model these as GraphQL types. If any of your mutations have a variable with bucket, key, region, mimeType and localUri fields, the SDK will upload the file to Amazon S3 for you.

但是,我无法将文件上传到我的 s3 存储桶。我知道该教程缺少很多细节。更具体地说,教程没有说NewPostMutation.js需要更改。

我按照以下方式更改了它:

import gql from 'graphql-tag';

export default gql`
mutation AddPostMutation($author: String!, $title: String!, $url: String!, $content: String!, $file: S3ObjectInput ) {
    addPost(
        author: $author
        title: $title
        url: $url
        content: $content
        file: $file
    ){
        __typename
        id
        author
        title
        url
        content
        version
    }
}
`

然而,即使在我实施了这些更改之后,文件仍未上传...

引擎盖下有一些活动部件,您需要确保在此 "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 我们想要为模型的 file 字段获取存储在 DynamoDB 中的 JSON 字符串并将其解析为 S3Object - 这个这样,当您查询模型时,您不会返回存储在 file 字段中的字符串,而是会得到一个包含 bucketregionkey 的对象可用于构建 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 上传和下载应该可以工作。

只是为了添加到讨论中。对于移动客户端,amplify(或者如果从 aws 控制台执行)会将突变调用封装到一个对象中。如果封装存在,客户端将不会自动上传。因此,您可以直接在 aws 控制台中修改突变调用,以便上传 file : S3ObjectInput 在调用参数中。这是我上次按照文档进行测试(2018 年 12 月)时发生的情况。

您将更改为此调用结构:

 type Mutation {
        createRoom(
        id: ID!,
        name: String!,
        file: S3ObjectInput,
        roomTourId: ID
    ): Room
}

而不是像这样自动生成的调用:

type Mutation {
    createRoom(input: CreateRoomInput!): Room
}
input CreateRoomInput {
    id: ID
    name: String!
    file: S3ObjectInput
}

完成此更改后,iOS 和 Android 都会很乐意上传您的内容,前提是您按照@hatboyzero 概述的方式进行操作。

[编辑] 我做了一些研究,据说这已在 2.7.3 https://github.com/awslabs/aws-mobile-appsync-sdk-android/issues/11 中修复。他们可能提到了 iOS 但我没有检查。