HOWTO get the attributes of an S3 file

Apr 9, 2008 at 9:21 PM
Hello...
Here is some naive code I wanted to share with you.

It's a modified version of the GetKeys ThreeSharpWrapper function that will build you an actual S3 file object (list).
Given a bucket name and a file name or file start.

The function is the following:

private ObjectKeyList GetKeysDetailed(string bucketName, string filter)
{
List<string> keys = new List<string>();
bool isTruncated = true;
string marker = string.Empty;
ObjectKeyList result = new ObjectKeyList();

bool onlyFirst1000 = cbxS3First1000Only.Checked;

while (isTruncated)
{
XmlDocument bucketXml = GetBucketListXml(bucketName, marker, filter);
XmlNodeList keyNodes = bucketXml.SelectNodes("//*local-name()='Key'");

foreach (XmlNode key in keyNodes)
{
ObjectKey oKey = new ObjectKey(key.InnerText);
result.Items.Add(oKey);

XmlNode internalKey = key.NextSibling;
while (internalKey != null)
{
if (internalKey.Name == "LastModified")
{
oKey.LastModified = DateTime.Parse(internalKey.InnerText);
}
else
{
if (internalKey.Name == "ETag")
{
oKey.ETag = internalKey.InnerText;
}
else
{
if (internalKey.Name == "Size")
{
oKey.SetSize(internalKey.InnerText);
}
else
{
if (internalKey.Name == "Owner")
{
//oKey.Owner = internalKey.Value;
}
else
{
if (internalKey.Name == "StorageClass")
{
oKey.StorageClass = internalKey.InnerText;
}
}
}
}
}
internalKey = internalKey.NextSibling;
}

keys.Add(key.InnerText);
}
if (keys.Count > 0)
{
marker = keyskeys.Count - 1;
}
isTruncated = bool.Parse(bucketXml.SelectSingleNode("//*local-name()='IsTruncated'").InnerText);
if (onlyFirst1000)
{
isTruncated = false;
}
}
return result;
}

You then need two more classes to add to the Affirma.ThreeSharp project for example.

////////////////////////////////// The list class...

using System;
using System.Collections.Generic;
using System.Text;

namespace Affirma.ThreeSharp.Model
{
/// <summary>
/// This represents a list of typed S3 file objects.
/// </summary>
public class ObjectKeyList
{
private Int32 _totalResults;

private readonly List<ObjectKey> _objectKeyList = new List<ObjectKey>();

public ObjectKeyList()
{
//
}

/// <summary>
/// Gets the total number of results on all pages.
/// </summary>
/// <value>The total number of results on all pages.</value>
public Int32 TotalResults
{
get
{
return _objectKeyList.Count;
}
internal set
{
_totalResults = value;
}
}

/// <summary>
/// Gets the items.
/// </summary>
/// <value>The items.</value>
public List<ObjectKey> Items
{
get
{
return _objectKeyList;
}
}
}
}


///////////////////// The object class in itself
using System;
using System.Collections.Generic;
using System.Text;

namespace Affirma.ThreeSharp.Model
{
/*
<Key xmlns="http://s3.amazonaws.com/doc/2006-03-01/">00580349-826f-48c1-ba9b-1b42db7b3ca8$0fc93308-45d8-493a-bb58-218e4121e70f.oldtag.mp3</Key>
<LastModified xmlns="http://s3.amazonaws.com/doc/2006-03-01/">2008-01-18T01:19:34.000Z</LastModified>
<ETag xmlns="http://s3.amazonaws.com/doc/2006-03-01/">"c8255530b599daeb7f88a832afb53d8a"</ETag>
<Size xmlns="http://s3.amazonaws.com/doc/2006-03-01/">1672</Size>
<Owner xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<ID>8fdb82b97e96ad32209d6e2e29cd01ddb9ffaa9b45e68230d3ec5e2dcc12c9b5</ID>
<DisplayName>musanadmin</DisplayName>
</Owner>
<StorageClass xmlns="http://s3.amazonaws.com/doc/2006-03-01/">STANDARD</StorageClass>
*/
/// <summary>
/// An object describing the owner of an S3 object.
/// </summary>
public class ObjectKeyOwner
{
private String _id;
private String _displayName;

/// <summary>
/// Initializes a new instance of the <see cref="ObjectKeyOwner"/> class.
/// </summary>
internal ObjectKeyOwner()
{
//
}

/// <summary>
/// Gets or sets the ID.
/// </summary>
/// <value>The ID.</value>
public string ID
{
get { return _id; }
set { _id = value; }
}

/// <summary>
/// Gets or sets the display name.
/// </summary>
/// <value>The display name.</value>
public string DisplayName
{
get { return _displayName; }
set { _displayName = value; }
}
}

/// <summary>
/// An object describing an online S3 object.
/// </summary>
public class ObjectKey
{
private String _key;
private DateTime _lastModified;
private String _etag;
private long _size;
private ObjectKeyOwner _owner;
private String _storageClass;

/// <summary>
/// Initializes a new instance of the <see cref="ObjectKey"/> class.
/// </summary>
internal ObjectKey()
{
//
}

public ObjectKey(string key)
{
_key = key;
}

/// <summary>
/// Gets or sets the key.
/// </summary>
/// <value>The key.</value>
public string Key
{
get { return _key; }
set { _key = value; }
}

/// <summary>
/// Gets or sets the last modified.
/// </summary>
/// <value>The last modified.</value>
public DateTime LastModified
{
get { return _lastModified; }
set { _lastModified = value; }
}

/// <summary>
/// Gets or sets the E tag.
/// </summary>
/// <value>The E tag.</value>
public string ETag
{
get { return _etag; }
set
{
if (value.StartsWith("\""))
{
_etag = value.Substring(1, value.Length - 2); // remove quotes
}
else
{
_etag = value;
}
}
}

/// <summary>
/// Gets or sets the size.
/// </summary>
/// <value>The size.</value>
public long Size
{
get { return _size; }
set { _size = value; }
}

/// <summary>
/// Sets the size.
/// </summary>
/// <param name="size">The size.</param>
public void SetSize(string size)
{
long onlineFileSize;
long.TryParse(size, out onlineFileSize);
Size = onlineFileSize;
}

/// <summary>
/// Gets the Md5 key of that object.
/// </summary>
/// <value>The MD5.</value>
public string MD5
{
get
{
StringBuilder sb = new StringBuilder();
//byte[] etag = StrToByteArray(ETag);
string etag = ETag.ToUpper();

for (int i = 0; i < etag.Length; i++)
{
if ((i > 0) && (i % 2) == 0)
{
sb.Append("-");
}
sb.Append(etagi);
}

string md5hash = sb.ToString();
return md5hash;
}
}

public ObjectKeyOwner Owner
{
get { return _owner; }
set { _owner = value; }
}

public string StorageClass
{
get { return _storageClass; }
set { _storageClass = value; }
}
}
}

That code does probably not scale well for non filtered queries. I don't know.

A sample call could be:
ObjectKeyList keys = GetKeysDetailed('thebucketname', 'myfilename_begining');

Use the code as you please.

I would love to see something similar integrated in 3# directly.

Also I would love if you could handle the MD5 hash in behalf of the user and return it after every possible call...

Anyway, I hope this can be useful to somebody.

Thank you for that excellent library.