BLOB stores (attributes of type Document on ORM level)

Entity attribute of Document type with storeName property for this attribute should be defined. Content (data stored in the database) of such attributes is a meta-information about file - a serialized BlobStoreItem object, not an actual file content.

In case entity is stored in the database using mStorage mixin, DDL generator create nvarchar(2000) field in database and store there BlobStoreItem serialized to JSON.

For Virtual entity developer should implement select method and fill BlobStoreItem manually (for example by parsing file content as done in ubm_form).

In the store definition section of application config developer describe stores. Each store must implement interface described below.

The store class itself provide storing and retrieving file content (based on meta-information stored in the entity attribute).

From client-side POV uploading files to server is separated onto two part. Like in gmail when you send mail with attachment you can attach a file, and gmail send it to server, but you do not send mail itself yet - this is first stage. Result of this stage is information about where file is stored on the server side. When you send email client pass to server email attributes, body and information about attached files. This is the same UnityBase do in the second stage.

Server side usage:

// get dirty (not committed yet) content of my_entity.docAttribute with ID = 12312 as ArrayBuffer
let tmpContent = App.blobStores.getContent(
   {ID: 12312, entity: 'my_entity', attribute: 'blobAttribute', isDirty: true},
   {encoding: 'bin'}
)

// get BLOB content of my_entity.docAttribute with ID = 12312 as base64 string
let base64Content = App.blobStores.getContent(
  {ID: 12312, entity: 'my_entity', attribute: 'blobAttribute'},
  {encoding: 'base64'}
)

// get BLOB content of my_entity.docAttribute with ID = 12312 as string
let base64Content = App.blobStores.getContent(
  {ID: 12312, entity: 'my_entity', attribute: 'blobAttribute'},
  {encoding: 'utf8'}
)

// read file and but it to BLOB store (not committed yet)
let content = fs.readFileSync(__filename, {encoding: 'bin'})
let fn = path.basename(__filename)
let blobItem = App.blobStores.putContent(
  {ID: 12312, entity: 'my_entity', attribute: 'blobAttribute'},
  content
)

// commit blob store
let dataStore = UB.DataStore(my_entity)
dataStore.run('update', {
  execParams: {
	ID: 12312,
	blobAttribute: JSON.stringify(blobItem)
  }
})

Upload file to server

In UnityBase upload file to server is performed in two stages:

  1. Upload file to temporary store - on this stage client call setDocument app level method and pass file content to server with an additional parameter isDirty=true, server store file in the temporary place.

  2. Client execute insert or update entity method and pass (with other attributes) string, returned on the first stage as a value of Document type attribute. On this stage server see what user want to update/insert Document and, based on Domain information, know what type of store is used for this attribute. Server:

  3. Finally, UnityBase update entity and commit database transaction (in case entity is non-virtual)

To enable support for client side large file uploading, set chunk size in mb in ubConfig.json, parameter uiSettings.uploadChunkSizeMb. Also, it's possible to pass different chunkSize to setDocument's params, or pass null or 0 to disable

UB.connection.setDocument(file, {
  entity: 'my_entity',
  attribute: 'blobAttribute',
  origName: 'img.jpg',
  filename: 'img.jpg',
  chunkSize: 10
})

Download file from server

For download file from server client call getDocument endpoint

Use S3 storage

For testing purpose we recommend to use a https://github.com/minio/minio

Methods

# getContent (requestBlobStoreRequest, optionsoptobject) → string | UBMail.TubSendMailAttachKind.Buffer | ArrayBuffer | null inner

Retrieve BLOB content from the blob store Return null in case attribute value is null.

Arguments:
  • request: BlobStoreRequest
  • options: object
    • encodingstring | null

      Possible values: 'bin' 'ascii' 'binary' 'hex' ucs2/ucs-2/utf16le/utf-16le utf8/utf-8 if null will return Buffer, if bin - ArrayBuffer

    • skipRlsboolean

      If true, use __skipRls/__skipAclRls flags when check entity existence

# getContentPath (requestBlobStoreRequest) → String | null inner

Retrieve full path to a file with BLOB content Returns:

  • null in case attribute value is null (document not assigned)
  • '' (empty string) in case store is not file based (reserved for future use)
  • full path to file on success
  • throws if parameters is invalid or document is not accessible

Arguments:

# getContentPathAndBlobInfo (requestBlobStoreRequest) → Object | null inner

Retrieve full path to a file with BLOB content Returns:

  • null in case attribute value is null (document not assigned)
  • '' (empty string) in case store is not file based (reserved for future use)
  • full path to file on success
  • throws if parameters is invalid or document is not accessible

Arguments:

# getDocumentEndpointInternal (reqTHTTPRequest, respTHTTPResponse, withBodyboolean) inner

Internal endpoint function which accepts additional boolean flag after req, resp arguments. Checks access to the document, obtains it content from blobStore and, by default, sends it to response.

Arguments:

# internalWriteDocumentToResp (requestParamsBlobStoreRequest, reqTHTTPRequest, respTHTTPResponse, skipRlsoptboolean) → Object inner

Writes a BLOB content to the response without verifying an ALS (but RLS is verified) or return an error without modifying a response.

SECURITY - method can be used inside endpoint or rest entity method, which already checks the access rights to the document

Returns:

  • {success: false, reason: 'fail reason description} if attribute value is empty or record with specified ID not found or not accessible or entity/attribute is unknown or id not passed etc.
  • {success: true} if content is written to the response

Arguments:

# markRevisionAsPermanent (requestBlobStoreRequest) inner

For a historical BLOB stores mark specified revision as a permanent. Permanents revisions will not be deleted during history rotation.

Arguments:
  
      const UB = require(@unitybase/ub')
const App = UB.App
App.blobStores.markRevisionAsPermanent({
  entity: 'my_entity',
  attribute: 'attributeOfTypeDocument',
  ID: 1000,
  revision: 2
})
  

# putContent (requestBlobStoreRequest, contentArrayBuffer) → BlobStoreItem inner

Server-side method for putting BLOB content to BLOB store temporary storage. Can accept a THTTPRequest as a content, in this case request body will be used as BLOB content

  
      // convert base64 encoded string stored in `prm.signature` to binary and put to the store
 docContent = App.blobStores.putContent({
   entity: 'iit_signature',
   attribute: 'signature',
   ID: ID,
   fileName: ID + '.p7s'
 }, Buffer.from(prm.signature, 'base64'))
  

# writeDocumentToResp (requestParamsBlobStoreRequest, reqTHTTPRequest, respTHTTPResponse, withBodyboolean) inner

Obtains the document content from a blobStore and sends it to the response

Arguments:

Types

# BlobStoreItem inner

Blob store item content (JSON stored in database)

Properties

  • v opt number

    Store version. Empty for UB<5. Store implementation must check v for backward compatibility

  • store string

    Code of store implementation from application config. If empty - use a store from attribute configuration

  • fName string

    File name inside store (auto generated in most case)

  • origName string

    Original file name (as user upload it)

  • relPath opt string

    Relative path of fName inside store folder (for file-based stores)

  • ct string

    Content type

  • size number

    Content size

  • md5 string

    Content MD5 checksum

  • revision opt number

    Content revision. Used only for stores with historyDepth > 0

  • deleting opt boolean

    If true content must be deleted/archived during commit

  • isDirty opt boolean

    ????

  • isPermanent opt boolean

    If true - do not delete content during history rotation

# BlobStoreRequest inner

Blob store request (parameters passed to get|setDocument)

Properties