If you’ve been using S3 client in the AWS SDK for .Net you might have noticed that there are no meth­ods that let you inter­act with the fold­ers in a bucket. As it turns out, S3 does not sup­port fold­ers in the con­ven­tional sense*, every­thing is still a key value pair, but tools such as Cloud Berry or indeed the Ama­zon web con­sole sim­ply uses ‘/’ char­ac­ters in the key to indi­cate a folder structure.

This might seem odd at first but when you think about it, there are no folder struc­ture on your hard drive either, it’s a log­i­cal struc­ture the OS pro­vides for you to make it eas­ier for us mere mor­tals to work with.

Back to the topic at hand, what this means is that:

  • if you add an object with key myfolder/ to S3, it’ll be seen as a folder
  • if you add an object with key myfolder/myfile.txt to S3, it’ll be seen as a file myfile.txt inside a myfolder folder, if the folder object doesn’t exist already it’ll be added automatically
  • when you make a Lis­tO­b­jects call both myfolder/ and myfolder/myfile.txt will be included in the result

Cre­at­ing folders

To cre­ate a folder, you just need to add an object which ends with ‘/’, like this:

public void CreateFolder(string bucket, string folder)
{
    var key = string.Format(@"{0}/", folder);
    var request = new PutObjectRequest().WithBucketName(bucket).WithKey(key);
    request.InputStream = new MemoryStream();
    _client.PutObject(request);
}

Here is a thread on the Ama­zon forum which cov­ers this technique.

List­ing con­tents of a folder

With the Lis­tO­b­jects method on the S3 client you can pro­vide a pre­fix require­ment, and to get the list of objects in a par­tic­u­lar folder sim­ply add the path of the folder (e.g. topfolder/middlefolder/) in the request:

var request = new ListObjectsRequest().WithBucketName(bucket).WithPrefix(folder);

If you are only inter­ested in the objects (includ­ing fold­ers) that are in the top level of your folder/bucket then you’d need to do some fil­ter­ing on the S3 objects returned in the response, some­thing along the line of:

// get the objects at the TOP LEVEL, i.e. not inside any folders
var objects = response.S3Objects.Where(o => !o.Key.Contains(@"/"));

// get the folders at the TOP LEVEL only
var folders = response.S3Objects.Except(objects)
                      .Where(o => o.Key.Last() == '/' &&
                                  o.Key.IndexOf(@"/") == o.Key.LastIndexOf(@"/"));
Share

7 Responses to “Working with S3 folders using the .Net AWS SDK

  1. Kamal says:

    Hi,

    That was really use­ful, but when I list the fold­ers in the root it does not dis­play any result though I have many fold­ers. The response does not dis­play fold­ers as sep­a­rate objects. It lists only the files within the folder. Why is it so? Do I need to spec­ify delim­iter or pre­fix? I could not find solu­tion to this after search­ing a lot. Help me to get out of this.

    Thanks,
    Kamal

  2. theburningmonk says:

    Hi Kamal,

    When you make the Lis­tO­b­jects request, to list the top level fold­ers, don’t set the pre­fix but set the delim­iter to ‘/’, then inspect the ‘Com­mon­Pre­fixes’ prop­erty on the response for the fold­ers that are in the top folder.

    To list the con­tents of a ‘root­folder’, make the request with pre­fix set to the name of the folder plus the back­slash, e.g. ‘rootfolder/’ and set the delim­iter to ‘/’. In the response you’ll always have the folder itself as an ele­ment with the same key as the pre­fix you used in the request, plus any sub­fold­ers in the ‘Com­mon­Pre­fixes’ property.

    Hope this helps!
    Yan

  3. Kamal says:

    Hi Yan,

    Thanks for your reply. That was really help­ful. Could you please guide me how to cre­ate a folder in Ama­zon S3 as Cloud­berry does?
    I tried many ways but noth­ing worked. Please give me sam­ple code so that it will be very use­ful to me. Help me get out of this issue.

    Thanks,
    Kamal

  4. theburningmonk says:

    Hi Kamal,

    All you need is some­thing like this (assum­ing that you’ve ref­er­enced the AWSSDK dll):

    var awsKey = “AWS key for your accout here”;
    var awsSe­cret = “AWS secret for your account here”;

    // by default, the s3 client will try to use HTTPS to talk to the ser­vice
    // if you don’t wanna have to deal with SSL then pass in a con­fig object
    // whose Com­mu­ni­ca­tion­Pro­to­col is set to HTTP
    var con­fig = new AmazonS3Config { Com­mu­ni­ca­tion­Pro­to­col = Protocol.HTTP };

    // cre­ate the client
    var client = Amazon.AWSClientFactory.CreateAmazonS3Client(awsKey, awsSe­cret, config);

    // make sure the key for the object you put ends with /, this needs to be an empty
    // object which is why in the next line I’m set­ting the input stream to a brand
    // new Mem­o­ryS­tream
    var request = new PutObjectRequest().WithBucketName(“bucketname”).WithKey(“testfolder/”);
    request.InputStream = new MemoryStream();

    // this will cre­ate the folder for you, which you can see in Cloud­berry
    client.PutObject(request);

  5. AJ says:

    Great, thanks for this. Was look­ing for the “pre­fix” method… that sorted me out a treat :)

  6. Si Hong says:

    Hi AJ/Yan:
    below is my codes, I got error “Object ref­er­ence not set to an instance of an object”. I know I have file in my bucket. Can you tell me what’s wrong here? Thanks…
    String­Builder out­put = new String­Builder();
    Lis­tO­b­ject­sRe­quest GetList = new Lis­tO­b­ject­sRe­quest()
    {
    Bucket­Name = “myBucket­Name”,
    Pre­fix = “images/”,
    Delim­iter = “/“
    };
    Lis­tO­b­ject­sRe­sponse response1 = s3.ListObjects(GetList);
    fore­ach (S3Object s3Object in response1.S3Objects)
    {
    output.AppendFormat(“{0}”, s3Object.Key);
    Response.Write(“output: ” + output.ToString());
    }

  7. Imran Aziz (@ManaKultras) says:

    Thanks!! That helped me

Leave a Reply