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

25

按照AWS AppSync文件中的这个文档/教程

文档中写道:

使用 AWS AppSync 可以将它们建模为 GraphQL 类型。如果您的任何变更具有带有 bucket、key、region、mimeType 和 localUri 字段的变量,则 SDK 将会为您将文件上传到 Amazon S3。

然而,我无法使我的文件上传到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
    }
}
`

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


我尝试使用了不同的方案,我应该使用哪一个? - SaidAkh
1
您需要AWS IAM凭证,我建议使用Cognito身份验证。如果您想在应用程序中轻松完成此操作,请查看AWS Amplify:https://github.com/aws/aws-amplify - Richard
2
谢谢@Richard!我会重新查看这个教程。我希望AWS Amazon的某个人在发布在线教程之前检查一下它们。我不得不进行很多调整才能使上述教程正常工作。 - SaidAkh
1
你能告诉我们需要怎样的调整吗?或者,可以发布一下你的工作代码吗? - Michael Economy
嗨@MichaelEconomy。抱歉没有表达清楚。在对话的背景下,我是指我已经让教程工作了,直到S3存储桶上传的那一刻。如果您需要帮助任何前面的部分,请告诉我。 - SaidAkh
显示剩余3条评论
2个回答

31

在这个 "just works" (TM) 的背后有一些需要注意的关键点。首先,您需要确保在 GraphQL schema 中定义了适当的输入和类型来支持 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对象。这样,在查询模型时,它会返回一个包含bucketregionkey属性的对象,你可以使用它们来构建访问S3对象的URL(直接通过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上传和下载应该可以正常工作。


1
我正在使用React Native而不是ReactJS。当我使用像imagepicker这样的东西时,如何从React Native加载图像复杂对象到AppSync?这里的示例https://docs.aws.amazon.com/appsync/latest/devguide/building-a-client-app-react.html仅提供<Input/>的示例,但没有使用imagepickers的示例。我尝试提供本地uri,但那样不起作用-文档已创建;然而,s3对象未上传。 - forJ
我并没有研究过这种方法是否可行,但是你可以使用一个隐藏的 <Input/> 元素来实现图片选择器,并让图片选择器调用一个方法来更新相应的文件对象到隐藏的 <Input/> 元素中。 - hatboyzero

2
只是为了增加讨论。对于移动客户端,放大(或者如果从AWS控制台进行)会将突变调用封装到一个对象中。如果存在封装,客户端不会自动上传。因此,您可以直接在AWS控制台中修改突变调用,以便在调用参数中包含上传文件: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
}

一旦您进行此更改,如果按照@hatboyzero所概述的方法操作,则iOS和Android都将愉快地上传您的内容。
[编辑] 我进行了一些研究,据说这个问题已经在2.7.3中得到解决https://github.com/awslabs/aws-mobile-appsync-sdk-android/issues/11。他们可能已经解决了iOS的问题,但我没有检查过。

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