Adding Attachments from a Folder

Someone asked us yesterday how to add all of the .jpg files from a folder into an Attachment field in a table. In this case, the individual wanted to add one attachment into a new record in the table. This could be pretty useful, so we thought we would post it more broadly.

This code accepts parameters for the folder name, table name containing the attachment, the name of the attachment field, and two optional arguments. The first optional argument is a pattern that can be used when searching a directory, such as "*.*". The second optional argument is a flag to indicate whether to iterate through subfolders. This argument is False by default. The code uses the FileSystemObject which is required on the machine. We're using it late bound so a reference to the Microsoft Scripting Runtime is not required.

Next, add the following routine to the module.

' -------------------------------------------------------------------------
' Procedure : AddAttachmentsFromFolder
' Purpose   : Adds one record to an attachment field for each file in the
'           : specified folder.
' Arguments : strFolder  - Name of the folder
'           : strTable   - Name of the table containing the attachment field
'           : strField   - Name of the attachment field
'           : strPattern - Search pattern for the directory. Defaults to
'           :              all files (*.*)
'           : fIncludeSubfolders - Include subfolders in the specified folder
'           :              Defaults to False
' Comments  : This routine adds a new record to the table, then one attachment.
'           : It commits the changes and handles all cleanup
' -------------------------------------------------------------------------
Sub AddAttachmentsFromFolder(ByVal strFolder As String, _
                             ByVal strTable As String, _
                             ByVal strField As String, _
                             Optional ByVal strPattern As String = "*.*", _
                             Optional ByVal fIncludeSubfolders As Boolean = False)


    Dim strFile      As String
    Dim lngCount     As Long
    Dim rstParent    As DAO.Recordset2
    Dim rstChild     As DAO.Recordset
    Dim fldAttach    As DAO.Field2
   
    Dim objFso       As Object ' Scripting.FileSystemObject
    Dim objFolder    As Object ' Scripting.Folder
    Dim objFile      As Object ' Scripting.File
    Dim objSubFolder As Object ' Scripting.Folder 

    On Error GoTo ErrorHandler

    ' Instantiate the FileSystemObject
    Set objFso = CreateObject("Scripting.FileSystemObject")

    ' fix up the folder
    If (Right(strFolder, 1) <> "\") Then strFolder = strFolder & "\" 

    ' make sure the folder exists
    If (Dir(strFolder, vbDirectory) = "") Then
        MsgBox "The specified folder does not exist: " & strFolder, vbExclamation
        Exit Sub
    End If

    ' get the folder object
    Set objFolder = objFso.GetFolder(strFolder)

    ' open the table containing the attachment field
    Set rstParent = CurrentDb().OpenRecordset(strTable)

    ' get the first file
    strFile = Dir(strFolder & strPattern)

    ' get each file that meets the pattern
    While (Len(strFile) > 0)
        ' add a record to the parent table
        Debug.Print strFolder & strFile
        rstParent.AddNew

        ' get the attachment recordset and FileData field to contain the file
        Set rstChild = rstParent.Fields(strField).Value
        Set fldAttach = rstChild.Fields("FileData")

        ' add the attachment to the attachment field
        rstChild.AddNew
        fldAttach.LoadFromFile strFolder & strFile
        rstChild.Update
        rstParent.Update

        ' get the next file
        strFile = Dir
    Wend

    ' recurse subfolders?
    If (fIncludeSubfolders) Then
        For Each objSubFolder In objFolder.SubFolders
            AddAttachmentsFromFolder objSubFolder.Path, strTable, strField, _
                                     strPattern, fIncludeSubfolders
       
        Next
    End If

Cleanup:
    rstParent.Close
    Set rstParent = Nothing
    Exit Sub

ErrorHandler:
    Debug.Print "Error " & Err.Number & " - " & Err.Description
    MsgBox Err.Description & vbCrLf & _
           Err.Number & vbCrLf & _
           Err.Source, VbMsgBoxStyle.vbCritical, "AddAttachmentsFromFolder Failed"
    GoTo Cleanup
End Sub

 

To test the routine, supply the name of a folder, as well as the name of a table and field. Here's a wrapper routine that does this. This example will load all .jpg files in the specified directory into a table called tblMyTable.


Sub TestAddAttachmentsFromFolder()
    Const strRootFolder As String = "<EnterFolderName>"
    AddAttachmentsFromFolder strRootFolder, "tblMyTable", "Attachments", _
        "*.jpg", True
    MsgBox "Done adding files from: " & vbCrLf & strRootFolder, _
        vbInformation, "File Import Process Completed"
End Sub

Office Blogs Comments

Comments: (6) Collapse

  • Shameless plug: for those who need to access network folders and want to avoid freezing their application if the network share isn't available for some reason, then they should have a look at my article about solving this issue: blog.nkadesign.com/.../ms-access-checking-network-paths-without-freezing-your-application Now, back to the article: Rob you should maybe comment on attaching files to a database and discuss in which case it is useful and which case it isn't. To me, attaching files can be useful in limited cases such as storing icons for the application or because the application itself demands it (keeping thumbnails of pictures or documents for instance or creating an archiving or versioning application). In many cases storing the document themselves is a bad idea: it bloats the database (you may reach the database size limit fairly quickly) and it makes accessing the documents entirely dependent on your application. There are other useful ways to manage documents:

    using hyperlinks and/or recording paths. A lot of application need to refer to documents on the filesystem.

    I usually like to record the name of the file (without its folder) and the path separately.

    The Path may even be broken into a Root path and a directory.

    The point of breaking these down is that if the whole document structure is moved somewhere else, you just need to update the root folder in the database. If you store complete hyperlinks to the file, you may have a harder time updating all these links as things move around. For instance, say you have a document at that location on your network: \\myserver\All Docs\Purchases\PO123.doc

    You could store the root path under a single record as: \\myserver

    You could record also a single directory for the Purchase Order: \All Docs\Purchases\

    Then record the filename of each document: PO123.doc Now when things move around, say you change server or decide to move all purchase documents to \Corporate Docs\Purchase & Logisitcis\ then you only need to update a single entry in your database. Opening a file from your application becomes as simple as reconstructing the full path: Root & PurchasingDirectory & DocumentFileName Anyway, just thought I would mention that there are many other ways to refer to documents without storing them in the database which, I think, should be avoided whenever possible.

  • I would never store pictures in a database. Bad idea!

  • Access 2007 question: How do I view add-in tab in Access 2007 CZ? The only help I can find is just how to list the add-ins. I don't need to list in the add-ins, I need to run them.

    Oh, this silly ribbon a nav pain... :-( :-( :-(

  • Renaud, no one mentioned you should store thousands of documents in attachements. This code is just an technical advise how to handle attachments. Instead of general retentions you could give an example on how much space an attached document needs. Say you have word docs with an average size of 50 kB. When stored in an attachment field they will be compressed additionally and may use about 20 kB per record. This means you can record about 50000 word docs before reaching a limit of 1 GB for the database.

    For JPEGs which won't be compressed we reach this limit depending of the picture dimensions maybe after 10000 records. Vladimir, yo never store pictures in databases?

    Well that's exactly what Sharepoint does. It stores any documents and pictures as MSSQL BLOBs behind the curtain. And this seems to work well. ;-)

  • Sascha Trowitzsch:

    "Vladimir, yo never store pictures in databases?"

    No! I use hyperlinks.

    "Well that's exactly what Sharepoint does."

    Sharepoint will not be used by professional DB developers due to the lack of referential integrity.

  • Sascha Trowitzsch:

    How about the following example: I have thousands of photos (JPG) each more than 2 MB in size. I want to handle them easily (copy, delete, edit in grahic editors, etc.). That's why I will not store them in database(s).

    BTW, Access' 1GB limit is "a day before yesterday's song". In these days Access database should store 100 GB... or even more!

Comments

Comments: (loading) Collapse