Save all attachments from selected folder, continued

In Save all attachments from selected folder, I posted a method for scrubbing an Outlook mail folder of file attachments in order to reduce your network footprint (or make your PST file smaller).

A couple of commenters asked if there's a way to make sure the filename is unique. There are two ways (I know of) to accomplish this goal, and we'll explore both in this post.

  1. Check the folder for an existing file with the same name. If the filename exists, start a Do Loop and add an incrementing number after the filename. Keep using the Dir function to check the newly created filename. Once Dir returns "", you have your filename. This is the hard way.
  2. Parse the filename and insert MailItem.ReceivedTime before the file extension. This is the easier way.

Method 1 – Loop through folder

Here's the original code, with a loop added to check the hard drive folder for an existing file with the same name. The loop keeps changing the filename with an incrementing number, until the Dir function returns an empty String. I've also modified the second parameter to default to False.

Sub SaveAllAttachments(ByVal folderName As String, Optional ByVal StripAttachments As Boolean = False)
' save all attachments from all emails in a folder to a folder on the hard disk
' optionally delete the attachments as well
' by Jimmy Pena, http://www.jpsoftwaretech.com 8/12/2009

Dim olfldr As Outlook.MAPIFolder
Dim itms As Outlook.Items
Dim newItems As Outlook.Items
Dim Msg As Outlook.MailItem
Dim MsgAttach As Outlook.attachments
Dim attachmentNumber As Integer
Dim i As Long
Dim newFilename As String

On Error GoTo ErrorHandler

' check if folder exists, if not then create it
' if folder cannot be created, exit
If Not FolderExists(folderName) Then
  MkDir folderName
End If

' check that folderName ends with "\"
If Right$(folderName, 1) <> "\" Then
  folderName = folderName & "\"
End If

' get default Inbox items collection
Set olfldr = GetDefaultFolder(olFolderInbox)
Set itms = olfldr.Items

' sort items by whether they have an attachment
itms.Sort "[Attachment]", False

' create subset of items collection
Set newItems = itms.Restrict("[Attachment] > 0")

' loop through items subset, save all attachments to disk folder
For Each Msg In newItems
  Set MsgAttach = Msg.attachments
  For attachmentNumber = MsgAttach.Count To 1 Step -1

    newFilename = MsgAttach.item(attachmentNumber).fileName

    If Dir(folderName & newFilename, vbDirectory) <> "" Then
        ' file already exists
      i = 0
      Do
        i = i + 1
        newFilename = ExtractFileName(MsgAttach.item(attachmentNumber).fileName) & i & _
            GetFileType(MsgAttach.item(attachmentNumber).fileName)
      Loop Until Dir(folderName & newFilename, vbDirectory) = ""
    End If

    MsgAttach.item(attachmentNumber).SaveAsFile _
      folderName & newFilename

    ' delete attachment (optional)
    If StripAttachments Then
      MsgAttach.item(attachmentNumber).Delete
    End If
  Next attachmentNumber
Next Msg

ProgramExit:
  Exit Sub
ErrorHandler:
  MsgBox Err.Number & " - " & Err.Description
  Resume ProgramExit
End Sub

Private Function GetDefaultFolder(outlookFolder As OlDefaultFolders) As Outlook.MAPIFolder
' returns MAPIFolder object from default folder list to calling program

Dim olapp As Outlook.Application
Dim olns As Outlook.NameSpace
Set olapp = Outlook.Application
Set olns = olapp.GetNamespace("MAPI")

Set GetDefaultFolder = olns.GetDefaultFolder(outlookFolder)

End Function

Function FolderExists(ByVal strPath As String) As Boolean
' from http://allenbrowne.com
    On Error Resume Next
    FolderExists = ((GetAttr(strPath) And vbDirectory) = vbDirectory)
End Function

Function ExtractFileName(fileName As String) As String
' extract filename portion of filename, no extension
Dim fileN As String

fileN = Right(fileName, Len(fileName) - InStrRev(fileName, "\"))
fileN = Replace(fileN, GetFileType(fileN), "")

ExtractFileName = fileN

End Function

Function GetFileType(fileName As String) As String
' get file extension

GetFileType = Mid$(fileName, InStrRev(fileName, "."), Len(fileName))

End Function

Method 2 – Append received time

This method relies on the fact that mail items are rarely received at the exact same time. If you need to be absolutely sure that you aren't overwriting a file, you're better off using the above technique. The chances are extremely small, but if the file exists you'll get an error (or worse, the file will be overwritten with no warning).

This code is identical to the one above, with the only difference being the removal of the Do loop, and the use of MailItem.ReceivedTime instead of the incrementing Long variable. Otherwise they're almost the same code.

Sub SaveAllAttachments(ByVal folderName As String, Optional ByVal StripAttachments As Boolean = False)
' save all attachments from all emails in a folder to a folder on the hard disk
' optionally delete the attachments as well
' by Jimmy Pena, http://www.jpsoftwaretech.com 4/28/2009

Dim olFldr As Outlook.MAPIFolder
Dim itms As Outlook.Items
Dim newItems As Outlook.Items
Dim Msg As Outlook.MailItem
Dim MsgAttach As Outlook.attachments
Dim attachmentNumber As Integer
Dim newFilename As String

On Error GoTo ErrorHandler

' check if folder exists, if not then create it
' if folder cannot be created, exit
If Not FolderExists(folderName) Then
  MkDir folderName
End If

' check that folderName ends with "\"
If Right$(folderName, 1) <> "\" Then
  folderName = folderName & "\"
End If

' get default Inbox items collection
Set olFldr = GetDefaultFolder(olFolderInbox)
Set itms = olFldr.Items

' sort items by whether they have an attachment
itms.Sort "[Attachment]", False

' create subset of items collection
Set newItems = itms.Restrict("[Attachment] > 0")

' loop through items subset, save all attachments to disk folder
For Each Msg In newItems
  Set MsgAttach = Msg.attachments
  For attachmentNumber = MsgAttach.Count To 1 Step -1

    newFilename = MsgAttach.item(attachmentNumber).fileName

    If Dir(folderName & newFilename, vbDirectory) <> "" Then
        ' file already exists
        newFilename = ExtractFileName(MsgAttach.item(attachmentNumber).fileName) & " " & Format(Msg.ReceivedTime, "MMDDYYYYHHMMSS") & _
            GetFileType(MsgAttach.item(attachmentNumber).fileName)
    End If

    MsgAttach.item(attachmentNumber).SaveAsFile _
      folderName & newFilename

    ' delete attachment (optional)
    If StripAttachments Then
      MsgAttach.item(attachmentNumber).Delete
    End If
  Next attachmentNumber
Next Msg

ProgramExit:
  Exit Sub
ErrorHandler:
  MsgBox Err.Number & " - " & Err.Description
  Resume ProgramExit
End Sub

Private Function GetDefaultFolder(outlookFolder As OlDefaultFolders) As Outlook.MAPIFolder
' returns MAPIFolder object from default folder list to calling program

Dim olApp As Outlook.Application
Dim olNS As Outlook.NameSpace
Set olApp = Outlook.Application
Set olNS = olApp.GetNamespace("MAPI")

Set GetDefaultFolder = olNS.GetDefaultFolder(outlookFolder)

End Function

Function FolderExists(ByVal strPath As String) As Boolean
' from http://allenbrowne.com
    On Error Resume Next
    FolderExists = ((GetAttr(strPath) And vbDirectory) = vbDirectory)
End Function

Function ExtractFileName(fileName As String) As String
' extract filename portion of filename, no extension
Dim fileN As String

fileN = Right(fileName, Len(fileName) - InStrRev(fileName, "\"))
fileN = Replace(fileN, GetFileType(fileN), "")

ExtractFileName = fileN

End Function

Function GetFileType(fileName As String) As String
' get file extension
' assumes there are no periods in filename
' (other than the one between the filename and extension)
GetFileType = Mid$(fileName, InStrRev(fileName, "."), Len(fileName))

End Function
About JP

I'm just an average guy who writes VBA code for a living. This is my personal blog. Excel and Outlook are my thing, with a sprinkle of Access and Word here and there. Follow this space to learn more about VBA. Keep Reading »



Share This Article:

Share and bookmark this articledelicious buttonfacebook buttonlinkedin buttonstumbleupon buttontwitter button

comment bubble 3 Comment(s) on Save all attachments from selected folder, continued:

  1. madmike writes:

    This is exactly what i am looking for, but i can't seem to get it to work. I have pasted the code into VBA, but can't seem to get outlook to see it, I am sure i am overlooking a very basic step. but anyhelp would be great!

  2. @madmike:

    Outlook won't "see" it because the procedure has parameters. You'll need to write a calling procedure(without parameters) that passes the parameters to the SaveAllAttachments procedure. Write another procedure like this:

    Sub CallMyFunction()
      Call SaveAllAttachments("C:my Files", True)
    End Sub

    Then you can use CallMyFunction as a macro for a toolbar button, or run it directly from Outlook by pressing Alt+F8. You can also write additional procedures to call SaveAllAttachments in different ways, and set up different buttons on the IDE.

    FYI I described this technique in a previous post: How to run a macro with parameters from a toolbar button in Outlook 2003.

  3. madmike writes:

    I was looking to apply this to a rule. I also did not want it to look at my inbox but a subfolder from my .PST. I got this to work somewhat but now i get an error that outlook can not do this action on this type of attachment.

This article is closed to new comments. Why?
Random Data Generator