Table of Contents

TMS FNC Cloud Storage Services Guides

TTMSFNCCloudStorageServices is a facade over the individual cloud storage components in TMS FNC Cloud Pack. Instead of coding against Box, Dropbox, Google Drive, hubiC or Microsoft OneDrive separately, you set the Service property once and use the same provider-neutral methods, items and events for all of them. That makes it the right starting point when the provider is a user preference, a deployment setting, or something you expect to change — and the wrong one when you need a provider-specific feature such as Dropbox sharing links or Google Drive export formats, which the dedicated components expose directly. This page covers connecting, the item model, the synchronous mode, resumable uploads, renaming, and the extra members the facade forwards from the underlying storage component.

Authentication and connecting

Set Service first, then the OAuth credentials of the app you registered with that provider on Authentication, then call Connect. Give PersistTokens.Section a non-empty value so Connect reuses or silently refreshes a stored session instead of opening the browser on every run; LoadTokens, SaveTokens and ClearTokens drive the same store manually. OnConnected fires once the session is usable — start the first request from there rather than immediately after Connect.

procedure TForm1.FormCreate(Sender: TObject);
begin
  // Service decides which provider the facade drives; every method below is
  // provider-neutral once this is set.
  TMSFNCCloudStorageServices1.Service := cssGoogleDrive;

  TMSFNCCloudStorageServices1.Authentication.ClientID := 'your-app-client-id';
  TMSFNCCloudStorageServices1.Authentication.Secret := 'your-app-secret';
  TMSFNCCloudStorageServices1.Authentication.CallBackURL := 'http://127.0.0.1:8000';

  // A non-empty Section persists the tokens, so a later Connect reuses or
  // silently refreshes the session instead of opening the browser again.
  TMSFNCCloudStorageServices1.PersistTokens.Section := 'MyAppStorage';

  TMSFNCCloudStorageServices1.OnConnected := StorageConnected;
  TMSFNCCloudStorageServices1.Connect;
end;

procedure TForm1.StorageConnected(Sender: TObject);
begin
  TMSFNCCloudStorageServices1.OnGetFolderList := StorageFolderListed;
  TMSFNCCloudStorageServices1.GetFolderList;
end;

procedure TForm1.StorageFolderListed(Sender: TObject; const AItems: TTMSFNCCloudItems;
  const ARequestResult: TTMSFNCCloudBaseRequestResult);
var
  I: Integer;
begin
  if not ARequestResult.Success then
    Exit;

  for I := 0 to AItems.Count - 1 do
    ListBox1.Items.Add(AItems[I].FileName);
end;

The item model

Every listing, search, upload and rename result is a TTMSFNCCloudItem, the provider-neutral description of a stored file or folder: FileName, Size, ItemType (ciFile or ciFolder), CreationDate, ModifiedDate, ID and ParentID. Requests are asynchronous by default: the method returns immediately and the outcome arrives in the matching On... event with a TTMSFNCCloudBaseRequestResult — check ARequestResult.Success before reading anything, and ARequestResult.ResultString for the error detail.

Resolving one item by ID

GetFileById fetches a single item from its provider ID without listing the folder that contains it, which is what you want when an ID was stored in your own database or came back from an earlier session. Unlike the listing calls it performs its request synchronously, so the returned item is usable straight away.

function TForm1.ResolveItem(const AID: string): string;
var
  Item: TTMSFNCCloudItem;
begin
  Result := '';

  // GetFileById performs its request synchronously, so the returned item is
  // usable straight away - no OnGetFolderList round trip is needed.
  Item := TMSFNCCloudStorageServices1.GetFileById(AID);
  if Assigned(Item) then
  begin
    Result := Item.FileName;
    if Item.ItemType = ciFolder then
      Result := Result + ' (folder)';
  end;
end;

Finding the containing folder with ParentID

TTMSFNCCloudItem.ParentID holds the provider's reference to the folder an item lives in — the parent's own ID on Box, Google Drive and OneDrive, and the parent path on Dropbox. Because the facade fills it in the same way for every provider, one loop can walk an item back up to the account root to build a breadcrumb or verify where a file ended up, without a folder listing per level.

function TForm1.BuildBreadcrumb(AItem: TTMSFNCCloudItem): string;
var
  Current: TTMSFNCCloudItem;
begin
  Result := AItem.FileName;
  Current := AItem;

  // ParentID is the provider-neutral reference to the containing folder, so
  // the same loop walks the tree on every configured Service.
  while Assigned(Current) and (Current.ParentID <> '') do
  begin
    Current := TMSFNCCloudStorageServices1.GetFileById(Current.ParentID);
    if Assigned(Current) then
      Result := Current.FileName + ' / ' + Result;
  end;
end;

Synchronous mode

Reach for synchronous mode when the calls are part of a batch job, a startup step, or a service routine rather than a responsive UI. BeginSync switches the component so every request blocks until the provider answers, which means the method's return value is filled in by the time it returns — GetFolderList hands back the items, CreateFolder hands back the new folder — and no event handler is needed. EndSync restores the asynchronous behaviour.

procedure TForm1.ListRootSynchronously;
var
  Items: TTMSFNCCloudItems;
  I: Integer;
begin
  // Sync functionality makes each request block until the provider answers,
  // so the method's return value is filled in when the call comes back.
  TMSFNCCloudStorageServices1.BeginSync;
  try
    Items := TMSFNCCloudStorageServices1.GetFolderList;
    if Assigned(Items) then
      for I := 0 to Items.Count - 1 do
        ListBox1.Items.Add(Items[I].FileName);
  finally
    // Always restore asynchronous behaviour, also on failure.
    TMSFNCCloudStorageServices1.EndSync;
  end;
end;

Because sync mode blocks the calling thread, never leave it on for work started from the UI thread of an interactive form, and always pair it with try ... finally EndSync so a failure cannot leave the component blocking.

Uploading large files

Upload sends a file in a single request, which every provider caps. A file above that cap goes through UploadResumableFile instead — a separate call, not something Upload switches to on its own. Describe the transfer with a TTMSFNCCloudFile entry taken from the CloudFiles collection and set its FilePath; the first call sends the first chunk and each following chunk is queued automatically from the previous one.

OnUploadResumableFile reports each completed chunk and is where a progress bar is driven from Position and GetFileSize. OnUploadResumableFileFinished fires after the last chunk, and OnUploadResumableFileFailed on a failed one. Because Position and SessionID survive a failure, handing the same TTMSFNCCloudFile back to UploadResumableFile resumes the transfer instead of restarting it.

procedure TForm1.UploadLargeFile(AFolder: TTMSFNCCloudItem);
var
  CloudFile: TTMSFNCCloudFile;
begin
  TMSFNCCloudStorageServices1.OnUploadResumableFile := StorageChunkUploaded;
  TMSFNCCloudStorageServices1.OnUploadResumableFileFinished := StorageUploadFinished;
  TMSFNCCloudStorageServices1.OnUploadResumableFileFailed := StorageUploadFailed;

  // The session is driven from a TTMSFNCCloudFile entry: FilePath is the local
  // file, and the folder item names the destination on the provider.
  CloudFile := TMSFNCCloudStorageServices1.CloudFiles.Add;
  CloudFile.FilePath := 'C:\local\database-backup.bak';

  // Sends the first chunk; each following chunk is queued automatically from
  // the completion of the previous one until the whole file has been sent.
  TMSFNCCloudStorageServices1.UploadResumableFile(CloudFile, AFolder);
end;

procedure TForm1.StorageChunkUploaded(Sender: TObject; const ACloudFile: TTMSFNCCloudFile;
  const ARequestResult: TTMSFNCCloudBaseRequestResult);
begin
  if ARequestResult.Success and (ACloudFile.GetFileSize > 0) then
    ProgressBar1.Value := ACloudFile.Position / ACloudFile.GetFileSize * 100;
end;

procedure TForm1.StorageUploadFinished(Sender: TObject; const ACloudFile: TTMSFNCCloudFile;
  const ARequestResult: TTMSFNCCloudBaseRequestResult);
begin
  if ARequestResult.Success then
    ShowMessage('Upload complete: ' + ACloudFile.FilePath);
end;

procedure TForm1.StorageUploadFailed(Sender: TObject; const ACloudFile: TTMSFNCCloudFile;
  const ARequestResult: TTMSFNCCloudBaseRequestResult);
begin
  // Position still holds the last confirmed offset, so the same CloudFile can
  // be handed to UploadResumableFile again to resume where it stopped.
  ShowMessage('Upload failed: ' + ARequestResult.ResultString);
end;

Renaming an item

RenameFile changes an item's name in place, keeping its location and its provider ID, and reports the renamed item in OnRenameFile. Use it rather than a delete-and-re-upload cycle: the ID stays valid, so references you stored elsewhere keep resolving.

procedure TForm1.RenameSelected(AItem: TTMSFNCCloudItem);
begin
  TMSFNCCloudStorageServices1.OnRenameFile := StorageRenamed;
  // Pass the new name only - the item keeps its location and its provider ID.
  TMSFNCCloudStorageServices1.RenameFile(AItem, 'Quarterly report 2026.pdf');
end;

procedure TForm1.StorageRenamed(Sender: TObject; const AItem: TTMSFNCCloudItem;
  const ARequestResult: TTMSFNCCloudBaseRequestResult);
begin
  if ARequestResult.Success then
    ShowMessage('Renamed to ' + AItem.FileName)
  else
    ShowMessage('Rename failed: ' + ARequestResult.ResultString);
end;

Reaching the underlying storage component

The facade forwards more than the core file operations. Storage returns the active TTMSFNCCloudStorage descendant itself, Drive the account root listing, and CloudFiles the collection the resumable session works from. Logging and LogFilename switch request tracing on for whichever provider is active, and MoveFileToRoot moves an item back to the account root without resolving the root folder first. Use Storage only for the rare member that has no facade equivalent — going through the facade keeps the code provider-neutral.

procedure TForm1.PrepareStorage;
var
  I: Integer;
begin
  // Logging and LogFilename are forwarded to the active provider component,
  // so request tracing is switched on without knowing which one it is.
  TMSFNCCloudStorageServices1.LogFilename := 'C:\logs\storage.log';
  TMSFNCCloudStorageServices1.Logging := True;

  // Drive holds the account root as it was filled in after connecting.
  for I := 0 to TMSFNCCloudStorageServices1.Drive.Count - 1 do
    ListBox1.Items.Add(TMSFNCCloudStorageServices1.Drive[I].FileName);

  // CloudFiles is the collection the resumable-upload session works from.
  Label1.Text := Format('%d pending upload(s)',
    [TMSFNCCloudStorageServices1.CloudFiles.Count]);

  // Storage exposes the provider component itself for the rare case where a
  // member has no facade equivalent.
  if Assigned(TMSFNCCloudStorageServices1.Storage) then
    Label2.Text := TMSFNCCloudStorageServices1.Storage.ClassName;
end;

procedure TForm1.MoveToRoot(AItem: TTMSFNCCloudItem);
begin
  TMSFNCCloudStorageServices1.OnMoveFile := StorageMoved;
  // MoveFileToRoot avoids having to resolve the account root folder first.
  TMSFNCCloudStorageServices1.MoveFileToRoot(AItem);
end;

procedure TForm1.StorageMoved(Sender: TObject;
  const ARequestResult: TTMSFNCCloudBaseRequestResult);
begin
  if not ARequestResult.Success then
    ShowMessage('Move failed: ' + ARequestResult.ResultString);
end;

Combining sync mode, ID lookup and rename

The features above compose. This routine resolves an item by ID, checks with ParentID that it sits in the expected folder, and renames it — all inside one BeginSync/EndSync block, so what would be three chained event handlers reads as sequential code:

procedure TForm1.NormalizeArchivedReport(const AItemID: string);
var
  Item, Parent: TTMSFNCCloudItem;
begin
  // Sync mode turns the resolve -> inspect -> rename chain into three plain
  // statements instead of three nested completion handlers.
  TMSFNCCloudStorageServices1.BeginSync;
  try
    Item := TMSFNCCloudStorageServices1.GetFileById(AItemID);
    if not Assigned(Item) or (Item.ItemType <> ciFile) then
      Exit;

    // ParentID lets the code decide from the item alone whether it still sits
    // in the folder it belongs to.
    Parent := TMSFNCCloudStorageServices1.GetFileById(Item.ParentID);
    if Assigned(Parent) and (Parent.FileName <> 'Archive') then
      Exit;

    if Pos('DRAFT ', Item.FileName) = 1 then
      TMSFNCCloudStorageServices1.RenameFile(Item, Copy(Item.FileName, 7, MaxInt));
  finally
    TMSFNCCloudStorageServices1.EndSync;
  end;
end;

Common mistakes

  • Reading a method's return value in async mode. Outside BeginSync/EndSync, only GetFileById returns usable data directly; everything else reports through its On... event.
  • Expecting Upload to chunk a large file. It does not — use UploadResumableFile, and handle OnUploadResumableFileFinished rather than OnUploadFile for that path.
  • Changing Service after connecting. Selecting another provider swaps the underlying storage component, so the previous session, Drive contents and item references no longer apply. Set Service, then connect.
  • Assuming ParentID is a path. It is whatever the provider uses to identify the parent; resolve it with GetFileById instead of parsing it.

See also