The Attachment modules provides:
  • Document imaging with file storage on a SAN, in the cloud (Amazon), or any other file store wrapped with the IFileObject interface
  • Mail merge capability via the IGenerate interface
  • Web controls supporting multiple-document uploads, drag-and-drop HTML5 and image previewing
  • Zip capability
  • Remote printing
  • Monitoring and processing of remote locations
Future enhancements planned include:
  • Bar code generation and scanning
  • Inbound faxing (with bar code scanning)
  • Integration with document delivery services (both physical and electronic) via IDelivery interface
  • Single attachment upload control
  • Multiple attachment upload control
  • Photo control (with lazy loader capabilities)

Document Imaging: IFileObject Interface

The Attachment module's main task is managing files associated with a QBO application. Over the course of time, files uploaded to a QBO site have been stored on:
  • A web server (but that was insufficient for load balancing situations and backups),
  • A SAN (but we eventually ran out of SAN space for clients storing hundreds of large images per property inspection),
  • On Amazon's S3 (where most of our client documents currently reside),
  • On a data server (to allow SQL direct manipulation of data files for import),
  • On a client FTP site, 
  • In a client imaging system, and
  • On the public internet
The various use cases above led to the development of the IFileObject interface. When a user attempts to upload or download a file from a QBO system, the Attachment module loads a plug-in that implements the IFileObject interface, and is coded to read or write streams of data to some underlying repository. Current IFileObject plugins include:
  • LocalFile (qbo.Attachment.FileObjects.LocalFile): reads and writes from a Windows file system using local paths
  • UNCFile (qbo.Attachment.FileObjects.UNCFile): reads and writes from a Windows file system using UNC paths (including credentials to access other servers)
  • S3FileObject (qbo.Attachment.Amazon.S3FileObject): reads and writes from Amazon's Simple Storage Service (S3)
  • Internet (qbo.Attachment.FileObjects.Internet): reads (but does not write) from the public internet
  • DistributedFile (qbo.Attachment.FileObjects.DistributedFile): writes to multiple locations
  • WebDAV (currently a QBO 2.0 plugin only): reads and writes from any WebDAV compliant imaging system
  • FTP (currently a QBO 2.0 plugin only): reads and writes from any FTP or FTPS site
  • sFTP (currently a QBO 2.0 plugin only): reads and writes from any sFTP site  
The IFileObject includes the following methods:
  • Read(streamToWriteTo, relativePath): reads a file into a stream
  • Write(streamToReadFrom, relativePath): writes a stream to a file
  • List(relativePath, pattern, depth): lists files that exist in a file store
  • Delete(relativePath): deletes a file
  • Rename(oldPath, newPath): renames a file
Each QBO system will include a configuration file (Config/FileObject.config) that defines the plugins used for the system. Note that a given plugin may be used more than once.

<FileObject Name="LocalFile" Type="qbo.Attachment.FileObjects.LocalFile, qbo.Attachment" Uri="C:\inetpub\local.quandis.net\Upload\LocalFileObject" Compression="false"/>
<FileObject Name="Template" Type="qbo.Attachment.FileObjects.LocalFile, qbo.Attachment" Uri="C:\inetpub\local.quandis.net" Compression="false"/>
<FileObject Name="Database"  Type="qbo.Attachment.FileObjects.UNCFile, qbo.Attachment" Uri="//" Compression="false"/>
<FileObject Name="AmazonS3" Type="qbo.Attachment.Amazon.S3FileObject, qbo.Attachment.Amazon" Uri="https://localhost-quandis-net.s3.amazonaws.com">
<Permission Method="ListFiles" Role="Administrators"/>
<FileObject Name="ClientABucket" Type="qbo.Attachment.Amazon.S3FileObject, qbo.Attachment.Amazon" Uri="https://clientA-quandis-net.s3.amazonaws.com">
<Permission Method="ListFiles" Function="ClientAListFiles"/>
<FileObject Name="Internet" Type="qbo.Attachment.FileObjects.Internet, qbo.Attachment" Uri="" Compression="false"/>

These low-level methods can be accessed from Attachment/FileObject.ashx directly. For example:
  • FileObject.ashx/ReadFile?FileObject=AmazonS3&Path=/Loan/12345/MyLoanDoc.pdf
  • FileObject.ashx/ListFiles?FileObject=AmazonS3&Path=/Loan/12345/&Pattern=*&Depth=0
  • FileObject.ashx/DeleteFile?FileObject=AmazonS3&Path=/Loan/12345/MyLoanDoc.pdf
Normally, however, files are manipulated at the same time Attachment rows are manipulated. For example:
  • When uploading a file, create an Attachment row, and stream the file to the appropriate location
  • When downloading a file, ensure the user has access to the corresponding Attachment row
  • When deleting an attachment, delete the underlying file as well
Attachment.ashx methods include:
  • Upload: used for HTML and Flash file uploads
  • UploadChunk: used for HTML5 uploads
  • View?ID=1988: used to view the document for AttachmentID 1988
  • Download?ID=1988: used to download the document for AttachmentID 1988
  • Write?ID=1988: 
    • writes the HttpContext.Request.InputStream to the file underlying AttachmentID 1988
  • Write?Object=Loan&ObjectID=12345: 
    • writes the InputStream to a new file, and creates a new Attachment under Loan 12345
  • CopyFile?ID=1988&&ID=1989&FileObject=ClientABucket&PathURL=/{Object}/{ObjectID}/{Guid}.{Uri.Extension}: 
    • copies a file to ClientABucket, using a path like /Loan/12345/e326f796-56d2-422b-b6f6-c568fbfb7fa2.pdf
    • the original file and Attachment record remain unchanged
  • TransferFile?ID=1988&&ID=1989&FileObject=AmazonS3&PathURL=/{Uri.Host}/{Year}-{Month}/{Uri.FileName}: 
    • copies the files underlying AttachmetnIDs 1988 and 1989 to AmazonS3
    • changes the PathURL to something like /images.sears.com/2012-05/MyOriginalFileName.jpg
    • modifies the Attachments to point to them
    • leaves the original files alone
  • MoveFile?ID=1988&&ID=1989&FileObject=AmazonS3: 
    • moves the files underlying AttachmetnIDs 1988 and 1989 from their current location to AmazonS3
    • leaves the PathURL alone
    • modifies the Attachments to point to them
    • deletes the original
  • See AttachmentObject.GetRelativePath() to see the string substitution you can use in PathURL