58
votes

Yes, I know. There is no folder concept on s3 storage. but I really want to delete a specific folder from s3 with node.js. I tried two solutions, but both didn't work. My code is below: Solution 1: Deleting folder directly.

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));
        });

Actually, I have a lot of files under folder2. I can delete single file from folder2 if I define key like this: var key='level/folder1/folder2/file1.txt', but it didn't work when I deleted a folder(key='level/folder1/folder2/'). Solution 2: I tried to set expiration to an object when I uploaded this file or folder to s3. code is below:

s3.client.putObject({
                Bucket: Camera_Bucket,
                Key: key,
                            ACL:'public-read', 
                Expires: 60 
            }

But it didn't either. After finishing uploading, I checked the properties of that file. it showed there was nothing value for expiry date:

Expiry Date:none
Expiration Rule:N/A

How can I delete folder on s3 with node.js?

8
Delete all the objects within the "folder" - datasage
I have a lot of files within the 'folder'. it was the reason I wanted to delete the folder. If I loop all the files and delete them, it will cost me long time to finish it. - user3034559
The folder only exists as a grouping of the paths that share that "folder" name. If you delete all the objects then the "folder" will no longer exist. If you are deleting alot of files, this may take awhile. The rest API has a command to delete up to 1000 files at a time, not sure if the node.js api exposes this. - datasage
How can I set the expiry date for a single object when I putObject onto s3. it didn't work when I made it as what aws doc described. it was weird. - user3034559
as far as I've seen, expiry works for all objects in a bucket. You should be able to run a listObjects with the prefix of the folder, and then a deleteObjects that takes an array of objects (at least in the PHP SDK). Links for the two calls: docs.aws.amazon.com/AWSSDKforPHP/latest/#m=AmazonS3/…, docs.aws.amazon.com/AWSSDKforPHP/latest/#m=AmazonS3/… - andreimarinescu

8 Answers

101
votes

Here is an implementation in ES7 with an async function and using listObjectsV2 (the revised List Objects API):

async function emptyS3Directory(bucket, dir) {
    const listParams = {
        Bucket: bucket,
        Prefix: dir
    };

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

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

    const deleteParams = {
        Bucket: bucket,
        Delete: { Objects: [] }
    };

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

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

    if (listedObjects.IsTruncated) await emptyS3Directory(bucket, dir);
}

To call it:

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

You can use aws-sdk module for deleting folder. Because you can only delete a folder when it is empty, you should first delete the files in it. I'm doing it like this :

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.IsTruncated) {
        emptyBucket(bucketName, callback);
      } else {
        callback();
      }
    });
  });
}
2
votes

According to accepted answer I created promise returned function, so you can chain it.

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;
        }
    });
}
1
votes

listObjectsV2 list files only with current dir Prefix not with subfolder Prefix. If you want to delete folder with subfolders recursively this is the source code: https://github.com/tagspaces/tagspaces/blob/develop/app/services/objectstore-io.ts#L838

  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;
  };
0
votes

According to Emi's answer I made a npm package so you don' t need to write the code yourself. Also the code is written in typescript.

See https://github.com/bingtimren/s3-commons/blob/master/src/lib/deleteRecursive.ts

0
votes

You can try this:

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

const s3Client = new S3() 

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

The accepted answer throws an error when used in typescript, and it is do Objects array in deleteParams. I made it work by modifying the code in the following way. I'm very new to Typescript but at least it is working now.

 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);
}
-4
votes

You can delete an empty folder the same way you delete a file. In order to delete a non-empty folder on AWS S3, you'll need to empty it first by deleting all files and folders inside. Once the folder is empty, you can delete it as a regular file. The same applies to the bucket deletion. We've implemented it in this app called Commandeer so you can do it from a GUI. enter image description here