如何使用node.js删除s3上的文件夹?

如何使用node.js删除s3上的文件夹?,node.js,amazon-web-services,amazon-s3,Node.js,Amazon Web Services,Amazon S3,是的,我知道。s3存储上没有文件夹概念。但是我真的想用node.js从s3中删除一个特定的文件夹。我尝试了两种解决方案,但都不起作用。 我的代码如下: 解决方案1: 直接删除文件夹 var key='level/folder1/folder2/'; var strReturn; var params = {Bucket: MyBucket}; var s3 = new AWS.S3(params); s3.client.listObjects({

是的,我知道。s3存储上没有文件夹概念。但是我真的想用node.js从s3中删除一个特定的文件夹。我尝试了两种解决方案,但都不起作用。 我的代码如下: 解决方案1: 直接删除文件夹

var key='level/folder1/folder2/';
var strReturn;
        var params = {Bucket: MyBucket};
        var s3 = new AWS.S3(params);
        s3.client.listObjects({
            Bucket: MyBucket,
            Key: key
        }, function (err, data) {
            if(err){
                strReturn="{\"status\":\"1\"}";

            }else{
                strReturn=+"{\"status\":\"0\"}";
            }
            res.send(returnJson);
            console.log('error:'+err+' data:'+JSON.stringify(data));
        });
实际上,我在folder2下有很多文件。如果我这样定义密钥,我可以从folder2中删除单个文件: var key='level/folder1/folder2/file1.txt',但当我删除一个文件夹(key='level/folder1/folder2/'时,它不起作用。 解决方案2: 当我将此文件或文件夹上载到s3时,我尝试将过期设置为对象。代码如下:

s3.client.putObject({
                Bucket: Camera_Bucket,
                Key: key,
                            ACL:'public-read', 
                Expires: 60 
            }
但也没有。完成上传后,我检查了该文件的属性。它显示失效日期没有任何值:

Expiry Date:none
Expiration Rule:N/A

如何使用node.js删除s3上的文件夹?

您可以使用aws sdk模块删除文件夹。由于只能在文件夹为空时删除该文件夹,因此应首先删除其中的文件。我是这样做的:

function emptyBucket(bucketName,callback){
  var params = {
    Bucket: bucketName,
    Prefix: 'folder/'
  };

  s3.listObjects(params, function(err, data) {
    if (err) return callback(err);

    if (data.Contents.length == 0) callback();

    params = {Bucket: bucketName};
    params.Delete = {Objects:[]};

    data.Contents.forEach(function(content) {
      params.Delete.Objects.push({Key: content.Key});
    });

    s3.deleteObjects(params, function(err, data) {
      if (err) return callback(err);
      if(data.Contents.length == 1000)emptyBucket(bucketName,callback);
      else callback();
    });
  });
}

下面是ES7中的一个实现,它具有
async
功能,并使用
listObjectsV2
(修订后的列表对象API):

称之为:

await emptyS3Directory(process.env.S3_BUCKET, 'images/')

根据已接受的答案,我创建了promise返回函数,所以您可以链接它

function emptyBucket(bucketName){
    let currentData;
    let params = {
        Bucket: bucketName,
        Prefix: 'folder/'
    };

    return S3.listObjects(params).promise().then(data => {
        if (data.Contents.length === 0) {
            throw new Error('List of objects empty.');
        }

        currentData = data;

        params = {Bucket: bucketName};
        params.Delete = {Objects:[]};

        currentData.Contents.forEach(content => {
            params.Delete.Objects.push({Key: content.Key});
        });

        return S3.deleteObjects(params).promise();
    }).then(() => {
        if (currentData.Contents.length === 1000) {
            emptyBucket(bucketName, callback);
        } else {
            return true;
        }
    });
}

根据Emi的回答,我做了一个npm包,这样你就不用担心了 我不需要自己写代码。代码也是用typescript编写的


请参见

删除空文件夹的方法与删除文件的方法相同。要删除AWS S3上的非空文件夹,您需要先删除其中的所有文件和文件夹以清空它。文件夹为空后,可以将其作为常规文件删除。这同样适用于bucket删除。我们已经在这个名为的应用程序中实现了它,所以你可以从GUI中实现它。 您可以尝试以下方法:

import { s3DeleteDir } from '@zvs001/s3-utils'
import { S3 } from 'aws-sdk'

const s3Client = new S3() 

await s3DeleteDir(s3Client, {
  Bucket: 'my-bucket',
  Prefix: `folder/`,
})

接受的答案在typescript中使用时抛出一个错误,它是deleteParams中的do Objects数组。我通过以下方式修改代码使其工作。我对打字很不熟悉,但至少现在可以用了

 async function emptyS3Directory(prefix: string) {
  const listParams = {
    Bucket: "bucketName",
    Prefix: prefix, // ex. path/to/folder
  };

  const listedObjects = await s3.listObjectsV2(listParams).promise();

  if (listedObjects.Contents.length === 0) return;

  const deleteParams = {
    Bucket: bucketName,
    Delete: { Objects: [] as any },
  };

  listedObjects.Contents.forEach((content: any) => {
    deleteParams.Delete.Objects.push({ Key: content.Key });
  });

  await s3.deleteObjects(deleteParams).promise();

  if (listedObjects.IsTruncated) await emptyS3Directory(prefix);
}

listObjectsV2仅列出具有当前目录前缀而不具有子文件夹前缀的文件。如果要递归删除包含子文件夹的文件夹,请参阅源代码:

deleteDirectoryPromise=async(路径:字符串):Promise=>{
const prefixes=wait this.getDirectoryPrefixes(路径);
如果(前缀.length>0){
常量deleteParams={
Bucket:this.config.bucketName,
删除:{对象:前缀}
};
返回此.objectStore.deleteObjects(deleteParams.promise();
}
返回此.objectStore
.deleteObject({
Bucket:this.config.bucketName,
关键字:路径
})
.promise();
};
/**
*递归获取所有aws目录前缀
*@param路径
*/
getDirectoryPrefixes=async(路径:字符串):Promise=>{
常量前缀=[];
常量承诺=[];
常量listParams={
Bucket:this.config.bucketName,
前缀:路径,
分隔符:'/'
};
const listedObjects=wait this.objectStore
.listObjectsV2(listParams)
.promise();
如果(
listedObjects.Contents.length>0||
listedObjects.CommonPrefixes.length>0
) {
listedObjects.Contents.forEach(({Key})=>{
前缀.push({Key});
});
listedObjects.CommonPrefixes.forEach(({Prefix})=>{
push({Key:Prefix});
promises.push(this.getDirectoryPrefixes(Prefix));
});
//if(listedObjects.IsTruncated)等待此.deleteDirectoryPromise(路径);
}
const subPrefixes=等待承诺。全部(承诺);
subPrefixes.map(前缀=>{
arrprofixes.map(前缀=>{
前缀。推送(前缀);
});
});
返回前缀;
};

删除“文件夹”中的所有对象“文件夹”中有很多文件。这就是我想删除文件夹的原因。如果我循环所有文件并删除它们,完成它将花费我很长时间。该文件夹仅作为共享该“文件夹”名称的路径分组存在。如果删除所有对象,则“文件夹”将不再存在。如果要删除大量文件,这可能需要一段时间。rest API有一个命令,可以一次删除多达1000个文件,但不确定node.js API是否公开了这一点。当我将对象放入s3时,如何设置单个对象的过期日期。当我按照aws文件所描述的那样做时,它不起作用。这很奇怪。就我所见,过期适用于桶中的所有对象。您应该能够运行一个带有文件夹前缀的listObjects,然后运行一个接受对象数组的deleteObjects(至少在PHP SDK中是这样)。两个调用的链接:,它受1000个对象的限制。因此,如果对象超过1000,您应该添加一些逻辑。
s3.deleteObjects
返回的数据中没有
data.Contents
参数。我认为应该改为
数据。删除了
。同意,
数据。内容
未定义。应该是
data.Deleted
。另外,如果(data.Contents.length==0)callback(),则行
更改为
要停止删除任何内容的尝试(否则AWS将返回错误,您将得到错误回调),我宁愿使用
data.Contents.IsTruncated
而不是
data.Contents.length==1000
来隐藏“内部”1000 var…工作起来很有魅力:)对于使用Meteor.js的用户,不要忘记在
Meteor.wrapAsync()
中包装异步函数。因此,在本例中,将此函数放在您的方法中:
Meteor.wrapAsync(emptyS3Directory(bucket,dir))
如果(listedObjects.IsTruncated)等待emptyS3Directory(bucket,dir),我相信应该是
而不是
if(listedObjects.Contents.IsTruncated)等待emptyS3Directory(bucket,dir)@Emi当您的函数返回时,是否保证删除所有对象?我担心S3删除操作的最终一致性,
 async function emptyS3Directory(prefix: string) {
  const listParams = {
    Bucket: "bucketName",
    Prefix: prefix, // ex. path/to/folder
  };

  const listedObjects = await s3.listObjectsV2(listParams).promise();

  if (listedObjects.Contents.length === 0) return;

  const deleteParams = {
    Bucket: bucketName,
    Delete: { Objects: [] as any },
  };

  listedObjects.Contents.forEach((content: any) => {
    deleteParams.Delete.Objects.push({ Key: content.Key });
  });

  await s3.deleteObjects(deleteParams).promise();

  if (listedObjects.IsTruncated) await emptyS3Directory(prefix);
}
  deleteDirectoryPromise = async (path: string): Promise<Object> => {
    const prefixes = await this.getDirectoryPrefixes(path);

    if (prefixes.length > 0) {
      const deleteParams = {
        Bucket: this.config.bucketName,
        Delete: { Objects: prefixes }
      };

      return this.objectStore.deleteObjects(deleteParams).promise();
    }
    return this.objectStore
      .deleteObject({
        Bucket: this.config.bucketName,
        Key: path
      })
      .promise();
  };

  /**
   * get recursively all aws directory prefixes
   * @param path
   */
  getDirectoryPrefixes = async (path: string): Promise<any[]> => {
    const prefixes = [];
    const promises = [];
    const listParams = {
      Bucket: this.config.bucketName,
      Prefix: path,
      Delimiter: '/'
    };
    const listedObjects = await this.objectStore
      .listObjectsV2(listParams)
      .promise();

    if (
      listedObjects.Contents.length > 0 ||
      listedObjects.CommonPrefixes.length > 0
    ) {
      listedObjects.Contents.forEach(({ Key }) => {
        prefixes.push({ Key });
      });

      listedObjects.CommonPrefixes.forEach(({ Prefix }) => {
        prefixes.push({ Key: Prefix });
        promises.push(this.getDirectoryPrefixes(Prefix));
      });
      // if (listedObjects.IsTruncated) await this.deleteDirectoryPromise(path);
    }
    const subPrefixes = await Promise.all(promises);
    subPrefixes.map(arrPrefixes => {
      arrPrefixes.map(prefix => {
        prefixes.push(prefix);
      });
    });
    return prefixes;
  };