Skip to main content

File attachments

Overview​

Virtual datamodels define file attachments using FileReference datatype, but the file content is physically located in ContentManager service. External application is using file upload / download functionality using REST API to manipulate with files and their content. See 'Services - Content Management' articles in 'Development documentation' section.

See also:

ContentManager API endpoints​

ContentManager API base URL: https://{hostname}/api/asol/cnt

GET methods​

1. Get a file-info by fileId​

GET /api/v1/Files/{id}/Info

  • parameters:
    • id - (mandatory) the file identifier
    • accessLevel - the data access level (Public = shared / Private = tenant, used for data integration)

2. Get a file-info by filePath​

GET /api/v1/Files/Info/Path/{path}

  • parameters:
    • path - (mandatory) the file path
    • accessLevel - the data access level (Public = shared / Private = tenant, used for data integration)
  • sample of response (shortened):
{
"metadata": {
"mimeType": "image/jpeg",
"length": 14410,
"hashCode": "JYq4fwSJY42fdMu2RDbr/5X2VoNu0zoKg4T08HIH7bhjhZKDc0bYT8tq5Bm3bc0jWovq3RBieeaUBqQV8QehTA=="
},
"name": "eureka.jpg",
"extension": ".jpg",
"sharingType": "Restricted",
"folderId": "1f117395-c0ed-4746-9a06-72515d1dbfb9",
"folder": {
"id": "1f117395-c0ed-4746-9a06-72515d1dbfb9",
"name": "Test",
"fullName": "ASOL/Test"
},
"fullName": "ASOL/Test/eureka.jpg",
"hiddenForPath": false,
"id": "1c6c91e4-5b9d-418e-a266-2fd481b9a4f5"
}

3. Get a file-content by fileId​

GET /api/v1/Files/{id}

  • parameters:
    • id - (mandatory) the file identifier
    • accessLevel - the data access level (Public = shared / Private = tenant, used for data integration)

4. Get a file-content by filePath​

GET /api/v1/Files/Path/{path}

  • parameters:
    • path - (mandatory) the file path
    • accessLevel - the data access level (Public = shared / Private = tenant, used for data integration)
  • sample of response headers (shortened):
   content-disposition: attachment; filename=eureka.jpg; filename*=UTF-8''eureka.jpg
content-length: 14410
content-type: image/jpeg
etag: "JYq4fwSJY42fdMu2RDbr/5X2VoNu0zoKg4T08HIH7bhjhZKDc0bYT8tq5Bm3bc0jWovq3RBieeaUBqQV8QehTA=="
last-modified: Tue,16 Feb 2021 14:22:36 GMT

POST methods​

1. Upload file​

POST /api/v1/FileUpload

  • parameters:
    • id - (mandatory) the file identifier
    • accessLevel - the data access level (Public = shared / Private = tenant, used for data integration)
  • sample of request headers (shortened):
   content-length: 14410
content-type: multipart/form-data; boundary=----WebKitFormBoundaryjWK4lVDs8qHYPQn4

------WebKitFormBoundaryjWK4lVDs8qHYPQn4
Content-Disposition: form-data; name="file"; filename="eureka.jpg"
Content-Type: image/jpeg
------WebKitFormBoundaryjWK4lVDs8qHYPQn4--
  • response properties:
    • uploadId - the unique identifier of uploaded file content
    • fileName - the filename with extension
    • fileExtension - the file extension
    • fileSize - the file size
    • mimeType - the mime type of file
    • uploadedBytes - the amount of uploaded bytes
  • sample of response:
{
"uploadId": "4d6f2614-89b7-420d-95d0-2c9b18b8d4a8",
"fileName": "eureka.jpg",
"fileExtension": ".jpg",
"fileSize": 14410,
"mimeType": "image/jpeg",
"uploadedBytes": 14410
}

2. Create file for uploaded file-content​

POST /api/v1/Files/{uploadId}

  • parameters:
    • id - (mandatory) the file identifier
    • accessLevel - the data access level (Public = shared / Private = tenant, used for data integration)
  • request body properties:
    • sharingType - (mandatory) the sharing type (Restricted = authenticated access, used for data integration)
    • folderId - (mandatory) the folder identifier
    • name - the filename with or without extension (the uploaded filename is used when not defined)
    • appendExtension: (optional) the flag to append original extension to filename
    • overwrite - (optional) the flag to overwrite file in filepath
    • hiddenForPath - (optional) the flag to hide file from filepath (file isn't accessible by filepath)
  • sample of request body:
{
"sharingType": "Restricted",
"folderId": "1f117395-c0ed-4746-9a06-72515d1dbfb9",
"name": "eureka.jpg",
"overwrite": true
}
  • api endpoint returns file-info in response, see: GET methods

3. Copy file from existing file and clone file-content​

POST /api/v1/Files/{id}/Copy

  • parameters:
    • id - (mandatory) the original file identifier
    • accessLevel - the original data access level (Private = tenant, used for data integration)
  • request body properties:
    • targetAccess - (mandatory) the target data access level (Private = tenant, used for data integration)
    • sharingType - (mandatory) the target sharing type (Restricted = authenticated access, used for data integration)
    • folderId - (mandatory) the target folder identifier
    • name - the target filename with or without extension (the original filename is used when not defined)
    • appendExtension: (optional) the flag to append original extension to target filename
    • overwrite - (optional) the flag to overwrite target file in filepath
    • hiddenForPath - (optional) the flag to hide file from filepath (file isn't accessible by filepath)
  • sample of request body:
{
"targetAccess": "Private",
"sharingType": "Restricted",
"folderId": "1f117395-c0ed-4746-9a06-72515d1dbfb9",
"name": "eureka.jpg",
"overwrite": true
}
  • api endpoint returns file-info in response, see: GET methods

ContentManager connector for backend usage​

  • nuget package: ASOL.ContentManager.Connector

Register ContentManager connector​

// dependency injection configuration
using ASOL.ContentManager.Connector.Extensions;
using ASOL.ContentManager.Connector.Options;

services.AddApiConnectorInfrastructure();

services.Configure<ContentManagerClientOptions>(Configuration.GetSection(nameof(ContentManagerClientOptions)));
services.AddContentManagerClient();
// appsettings.json
{
// ...

"ContentManagerClientOptions": {
"BaseUrl": "https://[hostname]/api/asol/cnt"
},

// ...
}

Consume data with file attachments​

Examples - get fileinfo with hashCode by fileId or filePath

using ASOL.ContentManager.Connector;
using ASOL.DataService.Edge.Contracts.DataTypes;

// get fileinfo and hashCode by fileId from ContentManager
var fileInfo = await cntClient.GetFileAsync(fileReference.Source.AccessLevel, fileReference.Source.FileId, acceptNotFound: false, ct: cancellationToken);
var hashCode = fileInfo.Metadata.HashCode;

// get fileinfo and hashCode by filePath from ContentManager
var fileInfo = await cntClient.GetFileByPathAsync(fileReference.Source.AccessLevel, fileReference.Source.FilePath, acceptNotFound: false, ct: cancellationToken);
var hashCode = fileInfo.Metadata.HashCode;

Examples - download filecontent by fileId or filePath

using ASOL.ContentManager.Connector;
using ASOL.DataService.Edge.Contracts.DataTypes;

// download filecontent by fileId from ContentManager (authenticated access for any of sharing types)
var fileContentResult = await cntClient.FileDownloadAsync(fileReference.Source.AccessLevel, fileReference.Source.FileId, cancellationToken);
using var stream = fileContentResult.ReadContentAsStreamAsync();

// download filecontent by filePath from ContentManager (authenticated access for any of sharing types)
var fileContentResult = await cntClient.FileDownloadByPathAsync(fileReference.Source.AccessLevel, fileReference.Source.FilePath, cancellationToken);
using var stream = fileContentResult.ReadContentAsStreamAsync();

Publish data with file attachments​

Examples - initialize folder structure by folderPath and get base folder for files

using ASOL.ContentManager.Connector;
using ASOL.ContentManager.Contracts;
using ASOL.ContentManager.Contracts.Primitives;
using ASOL.ContentManager.Contracts.Primitives.Filters;
using ASOL.Core.Multitenancy.Contracts;
using ASOL.Core.Paging.Contracts.Filters;
using ASOL.DataService.Edge.Contracts.DataTypes;

// recursive algorithm to create root path for your application
var folderPath = $"{applicationOwner}/{applicationCode}/{mandantCode}/{area}"; //recommended path
var accessLevel = DataAccessLevel.Private;
FolderModelBase folderInfo = null;
foreach (var folderName in folderPath.Split('/'))
{
var customFilter = new FolderSearchFilter { BaseFolderId = folderInfo?.Id, ExactPathMatch = true, FolderPath = folderName };
var folders = await cntClient.SearchFoldersAsync(accessLevel, customFilter, new PagingFilter { Limit = 1 }, cancellationToken);
folderInfo = folders.FirstOrDefault();
if (folderInfo == null)
{
folderInfo = await cntClient.CreateFolderAsync(accessLevel, new FolderModelCreate { Name = folderName, ParentId = folderInfo.Id }, cancellationToken);
}
}
var baseFolderId = folderInfo.Id;

Examples - upload and create file in Content Manager and prepare the file reference to the file

using ASOL.ContentManager.Connector;
using ASOL.ContentManager.Contracts;
using ASOL.ContentManager.Contracts.Primitives;
using ASOL.Core.Multitenancy.Contracts;
using ASOL.DataService.Edge.Contracts.DataTypes;

// upload filecontent into ContentManager
var uploadStatus = await cntClient.FileUploadAsync(accessLevel, fileName, stream, mimeType, cancellationToken);
var uploadId = uploadStatus.UploadId;

// create fileinfo in ContentManager
var fileRequest = new FileModelCreate { FolderId = rootFolderId, SharingType = SharingType.Restricted, Overwrite = true };
var fileInfo = await cntClient.CreateFileAsync(accessLevel, fileRequest, uploadId, cancellationToken);

// create file reference
var fileReference = new FileReference
{
FileName = preferredFileName,
ReferenceId = referenceId,
Source = new FileIdentifier { AccessLevel = accessLevel, FileId = fileInfo.Id, FilePath = fileInfo.FullName, FolderId = fileInfo.FolderId },
};

Examples - clone file in "consume to publish" scenario (instead of download and upload content to the new file)

using ASOL.ContentManager.Connector;
using ASOL.ContentManager.Contracts;
using ASOL.ContentManager.Contracts.Primitives;
using ASOL.Core.Multitenancy.Contracts;
using ASOL.DataService.Edge.Contracts.DataTypes;

// clone file instead of create file from downloaded and uploaded content
var originalFileReference = ... ; // obtained from consumed data
var copyRequest = new FileModelCopy { TargetAccess = targetAccessLevel, Name = targetFileName, FolderId = targetFolderId, SharingType = SharingType.Restricted, Overwrite = true };
var fileInfo = await cntClient.CopyFileAsync(originalFileReference.Source.AccessLevel, originalFileReference.Source.FileId, copyRequest, cancellationToken);