As you might expect, accessing a list item’s attachments in SharePoint isn’t straightforward. While SPListItem.Attachments returns a string-based list of attachment names, you’ll probably need to translate that into a URL at the very least, if not manipulate the attachment itself.
It helps to understand where SharePoint stores attachments after they’re uploaded. As usual, SharePoint Manager 2007 provides some insight:
Notice in the screenshot, I’m inspecting a list named “Announcements” and that list has one item (Hello Attachment—highlighted at the bottom). You’ll also notice there’s an Attachments folder sitting among the list items and it’s been expanded with to reveal another folder with the odd name of 2; below that you can actually see the attachment itself (My First Attachment.txt).
Before I get any further, I’ll point out the magical “2” folder isn’t magical at all: it relates to the list item in this list with the Id of 2—in this case, the Hello Attachment item (the first item in the list was deleted but, if it had attachments, there would be a corresponding “1” folder). It’s sensible to assume this structure exists to accommodate multiple attachments.
If you were to now try to access the Attachments folder in code using SPListItem.Folder, you’d be faced with a null reference exception. Instead, you need to ignore what SPM tells you about getting to the Attachments folder and arrive there instead using the parent list’s RootFolder property.
Assume you’re working within the scope of a foreach or have retrieved a list item via some other means:
SPList announcements = …Lists [“Announcements”]
foreach (SPListItem currentItem in announcements)
// we’re here
Before you do anything else, you’ll probably want to muck around and ensure you can actually access a list of attachments in some form. To identify the attachments by name, SPListItem.Attachment (SPAttachmentCollection) will return a collection of strings:
foreach (string currentAttachmentUrl in currentItem.Attachments)
At this point you’ve retrieved, from the example discussed earlier, a string like “My First Attachment.txt”—not of much use on its own. As mentioned, you’re probably after the full URL at the very least.
To access the actual attachment and not just it’s name, retrieve the list item’s parent list (SPListItem.ParentList) and then the parent list’s RootFolder. From there you can use the SubFolders collection to retrieve the “Attachments” SPFolder. Beware the SubFolders indexer expects the URL of the folder, not its name.
Finally, you need to retrieve the numbered folder specific to the list item. Here’s the complete mess:
SPFolder attachments = currentItem.ParentList.RootFolder.SubFolders ["Attachments"].SubFolders [currentItem.ID.ToString ()]
The SPFolder object’s URL property can now be prefixed to the attachment’s name retrieved previously or you can enumerate the SPFiles within the SPFolder.
Back in the day, Eric Shupps posted a way to do this by walking down the structure from the Lists folder. I think the above code is a slight improvement since the code’s a bit more manageable and you don’t need to retrieve the Lists folder or the parent list by name.
Ps. RootFolder is actually quite interesting as its Name property is actually the internal name of parent SPList object.