How to Store Binary Objects in the Microsoft Site Server Membership Directory

 

Craig Poxon

April 2000

Summary: Describes how to store binary attributes in the Microsoft Site Server Membership Directory using a combination of Microsoft Active Server Pages (ASP), Microsoft Visual Basic (VB), and Microsoft Active Directory Services Interface (ADSI). (9 printed pages)

Introduction

The most powerful part of Microsoft® Site Server 3.0 is arguably its directory service, the Membership Directory (MD). The MD is an open-standard, central data repository that stores user records containing personal profile information, which is used in personalization and membership (P&M) for any number of applications accessing it. All of the data stored against a user in the MD is currently text or integer based, but it is possible to store binary information. A binary storage capability allows the storing of objects such as images or certificates.

This article describes how to use binary attributes in Site Server with a combination of Microsoft Active Server Pages (ASP), Microsoft Visual Basic® (VB), and Microsoft Active Directory™ Services Interface (ADSI). As an example, we'll show how to store a large (greater than 16 KB) image in the MD.

Membership Directory Implementation and Normalization

Binary objects tend to be large, larger at least than the word fragments normally stored in an object in the Membership Directory. In his book, Site Server 3.0 Personalization and Membership, Robert Howard writes:

Any object that uses ADSI interfaces to access a directory has a cache that provides the requested information to the client. Instead of asking the LDAP Service to retrieve a value from the Membership Directory when it is requested, the entire contents of the attributes of the object are immediately available after the first Get of an individual attribute, or after calling GetInfo(). (p. 252)

Howard's comment implies that if a larger amount of information than normal is stored in an object in the MD, which is the case with binary attributes, performance may be affected, since all of the information is cached before the data can be accessed. With regularly used objects, such as members, this may be detrimental. The solution is to store the information in classes of their own and reference them in the parent object by use of a Distinguished Name (DN) attribute. The object can then be accessed (and cached) separately as it is needed, without the added overhead on a frequently used object such as a member.

In order to achieve this normalization, you'll need to make the following schema changes to the MD:

  1. Create a new multivalued binary schemaAttribute for storing data.

  2. Create a classSchema called "photoClass" to hold the binary schemaAttribute and its associated values.

  3. Create a Distinguished Name schemaAttribute to provide a link between the member and the instances of the photoClass object.

    Note   You may also consider adding the contentType schema attribute to the photoClass object so that you can store other information, such as the Multiple Internet Mail Extensions (MIME) type, for later use.

  4. Add the Distinguished Name schemaAttribute to the list of possible attributes for the member classSchema object.

  5. Create a container to hold the instances of the photoClass object.

Schema Issues

If we were to use the single-valued binary photograph attribute in the Site Server MD, we would not be able to store any more than 16 KB of data. From the Microsoft Commercial Internet System Resource Kit article, Microsoft Site Server 3.0 Membership Directory Configuration and Tuning Guidelines, we know that the binary attribute data is stored in the image type field "img_Val" in the Object_Attributes table of an MD database. Examining the design of the table in the database, we can see that 16 KB of space has been allocated for storage in this field. Since we want to store an image that is larger than 16 KB, we'll need to overcome this limitation, and so we must implement a multivalued binary syntax attribute. We'll then be able to store data in multiples of 16 KB within the same object.

Schema Changes

Now we'll look in detail at each of the necessary procedures for making schema changes to the MD.

To make the required schema changes

  1. To launch the Site Server Service Admin (MMC) tool, on the taskbar, click the Start button, point to Programs, Microsoft Site Server, and then click Administration.
  2. Right-click the Membership Directory Manager snap-in, and on the shortcut menu, click Preferences.
  3. Make sure that the Host Name and Port settings are correct for the Membership Directory you wish to modify.
  4. Expand the ou=Admin container. The cn=Schema container is now exposed.

To create the binary schemaAttribute

  1. Right-click the cn=Schema container, and on the shortcut menu, click New and then click Attribute. The New Attribute Wizard appears.
  2. Click Next. The Type the Name of the New Attribute dialog box appears.
  3. In the Name box, enter binary.
  4. Select the Multi-valued check box. The Display Name and Description boxes are optional. Click Next.
  5. In the Select the Attribute Syntax dialog box, click Binary, and then click Finish.

To create the photoClass classSchema

  1. In the left Console Tree pane of the MMC, right-click the cn=Schema container. On the shortcut menu, click New and then click Class. The New Class Wizard appears.

  2. Click Next.

  3. In the Name box, enter photoClass. The Display Name and Description boxes are optional. Click Next and click Next again.

    Note   Make sure that the Allow children option is not selected.

  4. In the Select the Attributes for the Class dialog box, click Add.

  5. In the Add Attribute dialog box, select the common-name attribute and click OK.

  6. In the first column of the Attributes table, select the Required Attribute check box for common-name.

  7. In the Naming Attribute text box, select cn.

  8. In the Select the Attributes for the Class dialog box, click Add.

  9. In the Add Attribute dialog box, select the binary attribute, click OK, and then click Finish.

To create the photo attributeSchema

  1. Right-click the cn=Schema container, and on the shortcut menu, click New and then click Attribute. The New Attribute Wizard appears.
  2. Click Next. The Type the Name of the New Attribute dialog box appears.
  3. In the Name box, enter photo.
  4. Select the Multi-valued check box. The Display Name and Description fields are optional. Click Next.
  5. In the Select the Attribute Syntax dialog box, select the Distinguished Name option and click Finish.

To add the new photo attributeShema to the member object

  1. In the left Console Tree pane of the MMC, click the cn=Schema container.
  2. In the right pane, in the list of attributes and classes, locate the cn=member classSchema object.
  3. Right-click the cn=member classSchema object and on the shortcut menu, click Properties.
  4. In the cn=member Properties dialog box, click the Class Attributes tab, and click Add.
  5. In the Add Attribute dialog box, select photo from the list of attributes and click OK.
  6. In the cn=member Properties dialog box, Click OK.

To create the Photographs container

  1. In the left-hand Console Tree pane of the MMC, right-click the Membership Directory Manager node.
  2. On the shortcut menu, click New and then click Container. The New Container Wizard appears.
  3. Click Next.
  4. In the Name box, click Photographs and click Finish.

Adding Binary Data

The trick to adding binary data to suitable attributes in the MD is that the information must be provided as a byte array. ASP pages are a fundamental part of the Site Server solution, unfortunately it cannot natively read files in a binary format.

The only file access available from ASP pages comes through Scripting.FileSystemObject, which can only provide a text stream. To enable ASP page access to a binary stream, we need to create a Microsoft ActiveX® DLL using Visual Basic.

Creating the ActiveX DLL

To create an ActiveX DLL using Visual Basic

  1. Launch Visual Basic.

  2. In the New Project dialog box, click the New tab and select ActiveX DLL.

  3. Copy and paste the following code into the Class1 code view.

    Function readFile(ByVal strFileName As String)
    
        Const intChunkSize = 16384
    
        Dim lngFileLen As Long
        Dim intFF, intChunk, intChunks As Integer
        Dim arrvarChunks() As Variant, arrbytChunk() As Byte
    
        intFF = FreeFile()
    
        lngFileLen = FileLen(strFileName)
    
        If lngFileLen < intChunkSize Then
            ReDim arrbytChunk(lngFileLen)
            Open strFileName For Binary Access Read As #intFF
            Get #intFF, , arrbytChunk
            ' Cast to an array so we can treat it as a collection
            readFile = Array(arrbytChunk)
        Else
    
            intChunks = lngFileLen \ intChunkSize
            ReDim arrvarChunks(intChunks)
    
            Open strFileName For Binary Access Read As #intFF 
            For intChunk = 0 To intChunks
    
                ' Arrays are zero-based so subtract one to account
                If intChunk = intChunks Then
                    ReDim arrbytChunk(lngFileLen Mod intChunkSize - 1)
                Else
                    ReDim arrbytChunk(intChunkSize - 1)
                End If
    
                Get #intFF, (intChunk * intChunkSize) + 1, arrbytChunk
                arrvarChunks(intChunk) = arrbytChunk
            Next
    
            ' Return variant byte array
            readFile = arrvarChunks
        End If
    
        Close #intFF
    
    End Function
    
  4. In the Project Explorer window, select the Project1 node and change the name in the Properties window to ASP.

  5. Select the Class1 leaf and change the name in the Properties window to ChunkBinaryFiles.

  6. On the File menu, click Save the Project.

  7. Save the Project as ChunkBinaryFiles.

  8. To create the ActiveX DLL, on the File menu click Make ChunkBinaryFiles.dll.

Using the ChunkBinaryFiles ActiveX DLL

We use the ActiveX DLL to slice up the data and store it in the binary attribute of a new photoClass object created in the Photographs container, which is linked to a member object by the photo attribute.

To use the ActiveX DLL to pass binary data to the MD

  1. Create an ASP page called binaryWrite.asp in the WWWRoot directory.

  2. Copy the following code into the ASP page:

    <%@ LANGUAGE="VBSCRIPT" %>
    <%
    
    Const ADS_PROPERTY_APPEND = 3
    Const LDAP_ALREADY_EXISTS = &H80071392
    
    strServer      = "server"
    strPort        = "1003"
    strRoot        = "o=Root"
    strAdmin       = "cn=Administrator,ou=Members," & strRoot
    strAdminPwd    = "password"
    strMember      = "cn=JohnSmith,ou=Members," & strRoot
    strContainer   = "ou=Photographs," & strRoot
    strFileName    = _ 
    "C:\Microsoft Site Server\SiteServer\Docs\ssasearch01.gif"
    strPath        = "LDAP://" & strServer & ":" & strPort & "/" 
    
    ' Connect to container
    Set objLDAP = GetObject("LDAP:")
    
    Set objContainer = objLDAP.OpenDSObject(strPath & strContainer, _ 
                strAdmin, strAdminPwd, 0)
    
    strCN = Right(strFileName, Len(strFileName) - _ 
    InstrRev(strFileName, "\"))
    
    Set objPhoto = objContainer.Create("photoClass", "cn=" & strCN)
    
    objPhoto.cn = strCN
    
    ' Detect error if instance already exists
    On Error Resume Next
    
    objPhoto.SetInfo
    
    If Err.Number = LDAP_ALREADY_EXISTS Then
    
       Response.Write "A photo with the name " & strCN & _
          " already exists in the " & objContainer.ADsPath & _
          " container."
    
       Response.End
    
    Else
    
       ' Break on any error
       On Error Goto 0
    
    End If
    
    Set objASPBinary = CreateObject("ASP.ChunkBinaryFiles")
    
    For Each chunk In objASPBinary.readFile(strFileName)
       objPhoto.PutEx ADS_PROPERTY_APPEND, "binary", Array(chunk)
       objPhoto.SetInfo
    Next
    
    ' Set DN pointer of the member to the image created
    Set objMember = objLDAP.OpenDSObject(strPath & strMember, strAdmin, _ 
    strAdminPwd, 0)
    objMember.Photo = "cn=" & strCN & "," & strContainer
    objMember.SetInfo
    
    ' Release the objects
    Set objMember      = Nothing
    Set objPhoto      = Nothing
    Set objContainer   = Nothing
    Set objLDAP        = Nothing
    Set objASPBinary    = Nothing
    
    %>
    

Note   If you added the contentType schema attribute to the photoClass object you should set the value after the new object has been created. Thus:

objPhoto.contentType = "image/gif"

Note   The preceding code includes an error check to see if an object with the same name already exists. You may wish to elaborate on this to do further error checking, but this is the most likely error to occur.

Since the ActiveX DLL returns a collection, the code simply iterates through the array, appending each chunk to the attribute. SetInfo must be called after each append, otherwise each append will be overwritten by the next. The Distinguished Name for the newly created object is added to the Member object so that it may be retrieved later.

Reading the Binary Data from the Membership Directory

The binary data stored in the MD must be retrieved to be utilized. With our image example, this is simply a matter of iterating through the values stored in the binary attribute and using the BinaryWrite method to write the specified information to the current HTTP output, which does so without any character conversion. This method is useful for writing nonstring information such as binary data.

To read the binary data from the MD and display it

  1. Create an ASP page called binaryRead.asp in the WWWRoot directory.

  2. Copy the following code into the ASP page:

    <%@ LANGUAGE="VBSCRIPT" %>
    <%
    
    strServer       = "server"
    strPort         = "1003"
    strRoot         = "o=Root"
    strAdmin        = "cn=Administrator,ou=Members," & strRoot
    strAdminPwd     = "password"
    strMember       = "cn=JohnSmith,ou=Members," & strRoot
    strPath         = "LDAP://" & strServer & ":" & strPort & "/" 
    
    ' Get reference to member
    Set objLDAP   = GetObject("LDAP:")
    Set objMember    = objLDAP.OpenDSObject(strPath & strMember, _ 
                strAdmin, strAdminPwd, 0)
    Set objPhoto     = objLDAP.OpenDSObject(strPath & objMember.Photo, _ 
                strAdmin, strAdminPwd, 0)
    
    arrData      = objPhoto.GetEx("binary")
    
    ' Clear out the existing HTTP header information
    Response.Expires = 0
    Response.Buffer = TRUE
    Response.Clear
    Response.ContentType = "image/gif"
    
    For Each chunk In arrData
       Response.BinaryWrite(chunk)   
    Next
    
    Set objPhoto   = Nothing
    Set objMember   = Nothing
    Set objLDAP   = Nothing
    
    %>  
    

Note   If you added contentType as an attribute to the photoClass classSchema and set the value, you should use it to set the ContentType for the Response. Thus:

Response.ContentType = objPhoto.contentType

The previous code only displays the image itself. It can be incorporated with an IMG tag in HTML:

<HTML>
<HEAD><TITLE>Display Membership Directory Image</TITLE></HEAD>
<BODY>
This page will display the image from a Site Server Membership Directory attribute.<BR>
<IMG SRC="binaryRead.asp">
</BODY>
</HTML>

Conclusion

In this article we have seen how to store large binary objects in the Membership Directory, allowing the association to members of such useful data as images or certificates. In order to do this we have:

  • Implemented a rationalized way to store similar information in the Membership Directory to maximize performance.
  • Overcome the physical limitation of the way binary data is stored in the underlying database.
  • Created an ActiveX DLL to enable ASP to read binary files.

To work further with binary attributes, you may want to try implementing the following:

  • Incorporating uploads to allow your users to easily attach binary data.
  • Modifying the binaryRead.asp to take a parameter, such as an identifier for the binary data you wish to return from the directory.

Note   Don’t forget to register your DLL using regsvr32.exe if you move it to another server.

If you have feedback or want additional information, please contact the author at craig@poxon.org.