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, onlyGetFileByIdreturns usable data directly; everything else reports through itsOn...event. - Expecting
Uploadto chunk a large file. It does not — useUploadResumableFile, and handleOnUploadResumableFileFinishedrather thanOnUploadFilefor that path. - Changing
Serviceafter connecting. Selecting another provider swaps the underlying storage component, so the previous session,Drivecontents and item references no longer apply. SetService, then connect. - Assuming
ParentIDis a path. It is whatever the provider uses to identify the parent; resolve it withGetFileByIdinstead of parsing it.
See also
- TTMSFNCCloudStorageServices — full class reference
- Get started
- Release notes