Přeskočit na hlavní obsah

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);