如何在Node.js GraphQL API上实现AWS S3功能

261 阅读11分钟

在Node.js GraphQL API上实现AWS S3功能

亚马逊网络服务(AWS)是亚马逊的一个子公司,提供云计算服务。这些服务围绕着存储、应用和基础设施展开。S3是由AWS提供的存储服务。AWS的Node.js SDK(软件开发工具包)使人们能够从Node.js应用程序中访问该平台提供的功能。

另一方面,GraphQL是一个服务器端的运行时间,用于根据在数据上定义的类型系统执行查询,提供查询语言。

目标

在这篇文章中,我们将使用Node.js AWS SDK实现以下s3功能。

  • 创建一个s3桶。
  • 取回已创建的s3桶。
  • 将单个对象上传到s3桶中。
  • 将多个对象上传到s3桶中。
  • 从s3 bucket获取上传的对象。
  • 从s3 bucket中删除单个对象。
  • 从s3 bucket中删除多个对象。
  • 删除一个s3 bucket。

前提条件

要跟上本文的进度,需要具备以下条件。

  • 在你的电脑上安装[Node.js]。
  • 在您的计算机上安装[Altair GraphQl客户端]。
  • 在你的电脑上安装了[Visual Studio Code]。
  • 一个AWS账户。如果你没有,请关注这[篇文章]。
  • 具有JavaScript的基本知识。
  • 使用[Apollo服务器]实现GraphQL API的基本知识。

获得你的安全凭证

要获得你的安全证书,请遵循以下步骤。

  • 前往[AWS控制台]。
  • 在右上角,点击Sign in to the console
  • 输入你的电子邮件地址,然后点击Next
  • 输入你的密码,然后点击Sign in
  • 在重定向的页面上,在搜索栏中搜索s3,并点击第一个结果。
  • 你将被重定向到你的s3账户页面。
  • 在右上方,有你的用户名和一个下拉箭头。点击这里,然后点击My security credentials
  • 在新页面上,点击Access keys (access key ID and secret access key)
  • 如果你已经有一个Access Key ID 和一个Secret access key ,请自由使用它们。但如果你没有,请点击Create New Access Key 按钮。
  • 下载Key File
  • 从当前的URL,你选择你的地区。例如,考虑到这个URL。https://console.aws.amazon.com/iam/home?region=us-east-2#/security_credentials,区域被设置为us-east-2。
  • 在你下载的Key File ,添加以下内容。
region=//the region from the URL. e.g us-east-2
  • 设置完地区后,你就可以进行下一步了。

设置开发服务器

在该仓库中,src 目录中,有开始和结束文件夹。在整个文章中,我们将在启动文件夹上工作。如果你遇到错误或想比较你的代码,请随时检查最终文件夹。

从你下载的Key File ,将数据适当地复制到.env 文件中。确保你的名字相匹配,这样就不会有冲突。

在克隆的文件夹中,运行以下命令来安装必要的依赖。

npm install

继续进入开始文件夹,浏览模式文件,了解我们是如何构建数据的。我们在整个文章中的重点将放在resolvers文件夹上。因此,掌握底层模式是有帮助的。

创建一个s3 bucket

一个s3 bucket是s3的基础存储实例。它由文件夹和对象组成。对象就是文件。桶中的数据被存储为文件夹或对象。

为了创建一个bucket,我们在createBucket() 功能下实现以下功能:resolvers/mutation-resolvers.js

//create a bucket.
async createBucket(bucketName){

    //create an object to hold the name of the bucket.
    const params = {
        Bucket:bucketName
    };

    //promisify the createBucket() function so that we can use async/await syntax.
    let create_bucket = promisify(this.s3.createBucket.bind(this.s3));

    //call the function to create the bucket.
    await create_bucket(params).catch(console.log);

    //return response to client.
    return {
        success:true,
        message:"Bucket created successfully."
    };

};

从上面的实现来看。

  • 创建params对象,其键值为Bucket ,以保持桶的名称。

  • 从s3答应createBucket() 函数,这样我们就可以使用async/await语法。

  • 通过params对象调用createBucket() 函数。

  • 送回一个与模式相匹配的响应。

为了测试这一点。

  • cmd+shift+\键,在你当前工作目录下打开终端。

  • 在弹出的终端中,运行以下命令。

npm run dev
  • 打开你的Altair GraphQl客户端。

  • 在URL部分,输入控制台中记录的URL。

  • 在工作区粘贴以下突变。

mutation CreateBucket {
  createBucket(bucketName:"simple-image-upload-bucket"){
    message
    success
  }
}
  • 随意改变bucketName 的值。

  • 点击播放按钮,检查结果。

  • 在创建了一个bucket之后,我们将获取已创建的buckets来验证bucket的创建。

取回已创建的s3桶

resolvers/query-resolvers.js 中,我们在fetchBuckets() 函数下增加了获取已创建的桶的功能。

//fetching buckets.
async fetchBuckets(){

    //promisify the listBuckets() so that we can use the async/await syntax.
    const listBuckets = promisify(this.s3.listBuckets.bind(this.s3));

    //get the buckets.
    let result = await listBuckets().catch(console.log);

    //loop through the result extracting only the name of each bucket.
    result = result.Buckets.map(result => result.Name);

    //return the bucket names as response to the client.
    return result;

};

从上面开始。

  • 从s3答应listBuckets() 函数,这样我们就可以使用async/await_语法。

  • 从s3中获取数据桶。

  • 将结果映射到模式中去。

为了测试这个。

  • 确保开发仍然在你的终端上运行。

  • 前往Altair GraphQl客户端,打开一个不同的标签,并在工作区粘贴以下查询。

query FetchBuckets{
  fetchBuckets
}
  • 点击播放按钮。新创建的桶应该出现了。

上传单个对象到s3桶中

在确认桶被成功创建后,现在是我们上传一些对象到桶的时候了。这些对象是文件。它们可以是图像、视频、音频、文本,以及更多。在这篇文章中,我们将专注于图像。请自由选择你想要的任何文件。

resolvers/mutation-resolvers.js ,在uploadObject() 功能下,我们增加了上传单个对象到s3桶的功能。

//upload object.
async uploadObject(file,bucketName){

  // create an object to hold the name of the bucket, key, body, and acl of the object.
    const params = {
        Bucket:bucketName,
        Key:'',
        Body:'',
        ACL:'public-read'
    };

    // obtain the read stream function and the filename from the file.
    let {createReadStream,filename} = await file;

    // read the data from the file.
    let fileStream = createReadStream();

    // in case of an error, log it.
    fileStream.on("error", (error) => console.error(error));

    // set the body of the object as data to read from the file.
    params.Body = fileStream;

    // get the current time stamp.
    let timestamp = new Date().getTime();

    // get the file extension.
    let file_extension = extname(filename);

    // set the key as a combination of the folder name, timestamp, and the file extension of the object.
    params.Key = `images/${timestamp}${file_extension}`;

    // promisify the upload() function so that we can use async/await syntax.
    let upload = promisify(this.s3.upload.bind(this.s3));

    // upload the object.
    let result = await upload(params).catch(console.log);

    // structure the response.
    let object = {
        key:params.Key,
        url:result.Location
    };

    // return the response to the client.
    return object;

};

从上面的片段来看。

  • 创建params对象,包括桶的名称、对象的键、对象的主体和对象的权限。

  • createReadStream ,并从文件中filename

  • 流入文件的内容。一旦出错,记录错误。

  • 将流的内容设置为对象的主体。

  • 使用特定的时间戳和其文件扩展名设置对象的键。该对象将被存储在images文件夹中。

  • 答应来自s3的upload() 函数,这样我们就可以使用async/await语法。

  • 调用发送params对象的函数。我们也从该函数中获得结果。

  • 构造一个样本对象以匹配模式输出。

为了测试这一点。

  • 确保开发服务器仍在从你的终端运行。

  • 前往Altair GraphQl Client,打开一个单独的标签,在工作区粘贴以下突变。

mutation ObjectUpload($object:Upload!) {
  uploadObject(file:$object,bucketName:"simple-image-upload-bucket"){
    url
    key
  }
}
  • $Object 是我们要从计算机中选择的动态对象,Upload 是它在GraphQl中的类型。

  • 要选择这个对象,点击下面的variables 。然后点击Add files 。在弹出的字段中,将名称从file 改为object 。然后点击右边的select files ,从计算机中选择任何对象。为了与文章保持一致,确保你选择了一张图片。

  • 在选择了一个对象后,点击播放按钮并检查结果。

  • 为了确认你的对象,在响应被送回后,复制从响应中送回的URL并将其粘贴到你的浏览器。你应该看到你的图像。

将多个对象上传到一个s3桶中

以同样的方式,我们将一个单一的对象上传到s3桶,我们也可以将多个对象上传到s3桶。

在同一个文件中,在uploadObjects() ,我们增加了上传多个对象的功能。

//upload objects.
async uploadObjects(files,bucketName){

    // create an object containing the name of the bucket, the key, body, and acl of the object.
    let params = {
        Bucket:bucketName,
        Key:'',
        Body:'',
        ACL:'public-read'
    };

    // structure the return data.
    let objects = [];

    // loop through all the sent files.
    for(let i = 0; i < files.length; i++){

        // Get that single file.
        let file = files[i];

        // From the file, get the read stream and the filename.
        let {createReadStream,filename} = await file;

        // read the data from the file.
        let stream = createReadStream();

        // in case of any error, log it.
        stream.on("error", (error) => console.error(error));

        // assign the body of the object to the data to read.
        params.Body = stream;

        // get the current timestamp.
        let timestamp = new Date().getTime();

        // get the file extension.
        let file_extension = extname(filename);

        // compose the key as the folder name, the timestamp, and the file extension of the object.
        params.Key = `images/${timestamp}${file_extension}`;

        // promisify the upload() function so that we can use async/await syntax.
        let upload = promisify(this.s3.upload.bind(this.s3));

        // upload the object.
        let result = await upload(params).catch(console.log);

        // push the structured response to the objects array.
        objects.push({
            key:params.Key,
            url:result.Location
        });

    };

    // return the response to the client.
    return objects;

};

从上面开始。

  • 创建params对象,包含要上传到的桶,对象的key,对象的body,以及读取文件的权限。

  • 初始化一个数组来保存作为响应的数据。

  • 循环浏览所有上传的对象。对于每个对象,我们读取其内容,将其设置为主体,获得时间戳和其文件扩展名作为其在图像文件夹中的键。将文件上传到s3桶,然后按照模式重组返回的数据,并将其推送到之前初始化的数组中。

  • 返回我们推送到的数组作为输出。

为了测试这个。

  • 确保开发服务器仍然在你的终端上运行。

  • 前往Altair GraphQl客户端,打开另一个标签,在工作区粘贴以下突变。

mutation ObjectsUpload($objects:[Upload!]!) {
  uploadObjects(files:$objects,bucketName:"simple-image-upload-bucket"){
    url
    key
  }
}
  • $objects 代表我们从计算机上传的动态对象。它们应该是多个,因此数组的类型是Upload 。该数组不应该是空的,而且没有一个文件应该是null 。我们还传入要上传到的桶的名称。如果你使用的是一个不同的桶的名字,请随意更改。

  • 要选择文件,点击左下角的Variables ,点击Add files 。在弹出的字段中,通过简单的点击,将标有1 的按钮切换为代表* 。将名称从file 改为objects 。在右边,点击select files 。使用你的电脑的command 键,选择多个对象。最好是只选择图像。选择后,你会看到你所选择的文件的数量与该领域相邻。

  • 点击播放按钮,检查结果。

  • 你可以通过访问从浏览器发回的每个URL作为响应来验证这些对象。

从s3桶中获取上传对象

在上传了你的对象之后,你应该能够取回这些对象。

resolvers/query-resolvers.js ,在fetchObjects() 功能下,我们将实现获取上传对象的功能。

//fetching objects.
async fetchObjects(bucketName){

    // create an object to hold the name of the bucket.
    const params = {
        Bucket:bucketName
    };

    // promisify the listObjects() function so that we can use the async/await syntax.
    let getObjects = promisify(this.s3.listObjects.bind(this.s3));

    // get the objects.
    let result = await getObjects(params).catch(console.log)

    // come up with the array to be returned.
    let objects = [];

    // Loop through each object returned, structuring the data to be pushed to the objects array.
    result.Contents.forEach( content => {
        return objects.push({
            key:content.Key,
            url:getUrl.bind(this,bucketName,content.Key)
        })
    } );

    // return response to the client.
    return objects;

};

从上面开始。

  • 创建params对象,其键值为Bucket ,代表我们要从其中获取上传对象的桶名。

  • 从s3答应listObjects() ,这样我们就可以使用async/await语法了。

  • 抓取对象。

  • 循环处理发回的结果,按照模式构建数据以匹配样本输出,然后发回结果。

为了测试这一点。

  • 确保开发服务器在你的终端上运行。

  • 进入Altair GraphQl客户端,打开一个单独的标签,在工作区粘贴以下查询。

query FetchObjects {
  fetchObjects(bucketName:"simple-image-upload-bucket"){
    url
    key
  }
}
  • 如果你使用的是不同的桶名,请随意更改。

  • 点击播放按钮,检查结果。

  • 随意复制任何对象的URL,粘贴在浏览器中并查看该对象。

从s3桶中删除一个对象

一旦一个对象不再需要了,为了释放空间,你可以随时从桶中删除它。

resolvers/mutation-resolvers.js ,在deleteObject() 函数下,我们实现了从s3桶中删除一个对象的功能。

//delete object.
async deleteObject(bucketName,key){

    // create an object to hold the name of the bucket, and the key of an object.
    const params = {
        Bucket:bucketName,
        Key: key
    };

    // promisify the deleteObject() so that we can use the async/await syntax.
    let removeObject = promisify(this.s3.deleteObject.bind(this.s3));

    // remove the object.
    await removeObject(params).catch(console.log);

    // send back a response to the client.
    return {
        success:true,
        message:"Object successfully deleted."
    };

};

从上面。

  • 创建一个params对象,其键值为bucket名称和对象的具体键值。

  • 从s3答应deleteObject() ,这样我们就可以使用async/await的语法。

  • 删除该对象。

  • 按照模式送回响应。

为了测试这个。

  • 确保开发服务器仍然在你的终端上运行。

  • 前往Altair GraphQl客户端,打开一个单独的标签,并在工作区粘贴以下突变。

mutation DeleteObject {
  deleteObject(bucketName:"simple-image-upload-bucket",key:"enter key of the object"){
    success
    message
  }
}
  • 在我们获取对象的标签中,从那里复制任何键,并粘贴到key 参数值。

  • 如果你使用的是一个单独的桶的名称,请确保你改变这个名称。

  • 点击播放按钮,检查结果。

从一个s3桶中删除多个对象

删除多个不再相关的对象,可以释放桶中的空间。

在同一个文件中,在deleteObjects() 功能下,我们实现了删除多个对象的功能。

//delete objects.
async deleteObjects(bucketName,objectKeys){

    // create an object to hold the name of the bucket and the objects to be deleted.
    const params = {
        Bucket:bucketName,
        Delete:{
            Objects:[]
        }
    };

    // Loop through all the object keys sent pushing them to the params object.
    objectKeys.forEach((objectKey) => params.Delete.Objects.push({
        Key:objectKey
    }));

    // promisify the deleteObjects() function so that we can use the async/await syntax.
    let removeObjects = promisify(this.s3.deleteObjects.bind(this.s3));

    // remove the objects.
    await removeObjects(params).catch(console.log);

    // send back a response to the server.
    return {
        success:true,
        message:"Successfully deleted objects"
    };

};

从上面开始。

  • 创建一个params对象,在Bucket key下包含要删除的桶的名称,在Delete key下包含要删除的Objects 的数组。

  • 循环通过objectKeys ,在params 对象中填充Objects 数组的关键字Delete

  • 从s3答应deleteObjects() ,这样我们就可以使用async/await语法。

  • 删除这些对象。

  • 按照模式送回一个响应。

为了测试这个。

  • 确保开发服务器已经启动并从你的终端运行。

  • 前往Altair GraphQl客户端,打开一个单独的标签,在工作区粘贴以下突变。

mutation DeleteObjects {
  deleteObjects(
    bucketName:"simple-image-upload-bucket",
    objectKeys:["enter_object_key","enter_object_key"]
  ){
    success
    message
  }
}
  • 从获取对象的标签中,复制并粘贴至少两个对象的键到objectKeys 数组中。如果你有更多的对象,请随意包括更多。

  • 如果你使用的是一个不同的桶的名字,请确保你改变它。

  • 点击播放按钮,检查结果。

删除一个s3 bucket

如果一个s3 bucket已经没有用处了,你可以随时删除它。但是要删除一个s3 bucket,你必须确保里面没有任何对象。否则就会失败。

在同一个文件中,在deleteBucket() 函数下,我们实现了删除一个桶的功能。

//delete bucket.
async deleteBucket(bucketName){

    // create an object to hold the name of the bucket.
    const params = {
        Bucket:bucketName
    };

    // promisify the deleteBucket() so that we can use the async/await syntax.
    let removeBucket = promisify(this.s3.deleteBucket.bind(this.s3));

    // remove the bucket.
    await removeBucket(params).catch(console.log);

    // send back a response to the client.
    return {
        success:true,
        message:"Successfully deleted bucket"
    }

};

从上面开始。

  • 创建一个params对象,在Bucket 关键下保存要删除的桶的名称。

  • 答应deleteBucket() 方法,这样我们就可以使用async/await语法。

  • 删除水桶并发回一个响应。

为了测试这个。

  • 确保开发服务器在你的终端上运行。

  • 前往Altair GraphQl Client,打开一个单独的标签,并在工作区粘贴以下突变。

mutation DeleteBucket {
  deleteBucket(bucketName:"enter-a-bucket-name"){
    success
    message
  }
}
  • 如果你有一个桶,并且它有文件,通过创建一个s3桶并创建一个假桶。

  • 在突变中,在bucketName 值中粘贴其名称。

  • 点击播放按钮,检查结果。

总结

到目前为止,AWS S3桶是一个常用的云存储服务。它提供了敏捷性,能够对对象进行各种操作。将对象上传到云存储服务,比用大量数据淹没你的服务器要好。