From 6eb0a309847c6c128f8f178825008cc76db4ff48 Mon Sep 17 00:00:00 2001 From: Annie_wang Date: Thu, 28 Apr 2022 10:04:16 +0800 Subject: [PATCH] update docs Signed-off-by: Annie_wang --- .../database/database-mdds-guidelines.md | 408 ++++++++---------- en/readme/distributed-file.md | 269 ------------ en/readme/file-management.md | 10 +- 3 files changed, 183 insertions(+), 504 deletions(-) delete mode 100644 en/readme/distributed-file.md diff --git a/en/application-dev/database/database-mdds-guidelines.md b/en/application-dev/database/database-mdds-guidelines.md index b335953d1a..d59fd3904a 100644 --- a/en/application-dev/database/database-mdds-guidelines.md +++ b/en/application-dev/database/database-mdds-guidelines.md @@ -1,233 +1,181 @@ -# Distributed Data Service Development - -## When to Use - -The DDS implements synchronization of application data across user devices. When data is added, deleted, or modified for an application on a device, the same application on another device can obtain the data changes. The DDS applies to the distributed gallery, messages, contacts, and file manager. - -## Available APIs - -The following table describes the APIs provided by the OpenHarmony DDS module. - -**Table 1** APIs provided by the DDS - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Function

-

API

-

Description

-

Creating a distributed database

-

createKVManager(config: KVManagerConfig, callback: AsyncCallback<KVManager>): void

-

createKVManager(config: KVManagerConfig): Promise<KVManager>

-

Creates a KVManager object for database management.

-

getKVStore<T extends KVStore>(storeId: string, options: Options, callback: AsyncCallback<T>): void

-

getKVStore<T extends KVStore>(storeId: string, options: Options): Promise<T>

-

Obtains the KV store with specified Options and storeId.

-

Managing data in a distributed database

-

put(key: string, value: Uint8Array | string | number | boolean, callback: AsyncCallback<void>): void

-

put(key: string, value: Uint8Array | string | number | boolean): Promise<void>

-

Inserts and updates data.

-

delete(key: string, callback: AsyncCallback<void>): void

-

delete(key: string): Promise<void>

-

Deletes data.

-

get(key: string, callback: AsyncCallback<Uint8Array | string | boolean | number>): void

-

get(key: string): Promise<Uint8Array | string | boolean | number>

-

Queries data.

-

Subscribing to changes in the distributed data

-

on(event: 'dataChange', type: SubscribeType, observer: Callback<ChangeNotification>): void

-

on(event: 'syncComplete', syncCallback: Callback<Array<[string, number]>>): void

-

Subscribes to data changes in the database.

-

Synchronizing distributed data

-

sync(deviceIdList: string[], mode: SyncMode, allowedDelayMs?: number): void

-

Triggers database synchronization in manual mode.

-
- -## How to Develop +# Distributed Data Service Development + +## When to Use + +The Distributed Data Service (DDS) implements synchronization of application data across user devices. When data is added, deleted, or modified for an application on a device, the same application on another device can obtain the updated data. The DDS applies to the distributed gallery, messages, contacts, and file manager. + + +## Available APIs + +The table below describes the APIs provided by the OpenHarmony DDS module. + +**Table 1** APIs provided by the DDS + +| Category | API | Description | +| -------------------------- | ------------------------------------------------------------ | ----------------------------------------------- | +| Creating a distributed database | createKVManager(config: KVManagerConfig, callback: AsyncCallback<KVManager>): void
createKVManager(config: KVManagerConfig): Promise<KVManager> | Creates a **KVManager** object for database management.| +| Obtaining a distributed KV store | getKVStore<T extends KVStore>(storeId: string, options: Options, callback: AsyncCallback<T>): void
getKVStore<T extends KVStore>(storeId: string, options: Options): Promise<T> | Obtains the KV store with the specified **Options** and **storeId**.| +| Managing data in a distributed KV store| put(key: string, value: Uint8Array \| string \| number \| boolean, callback: AsyncCallback<void>): void
put(key: string, value: Uint8Array \| string \| number \| boolean): Promise<void> | Inserts and updates data. | +| Managing data in a distributed KV store| delete(key: string, callback: AsyncCallback<void>): void
delete(key: string): Promise<void> | Deletes data. | +| Managing data in a distributed KV store| get(key: string, callback: AsyncCallback<Uint8Array \| string \| boolean \| number>): void
get(key: string): Promise<Uint8Array \| string \| boolean \| number> | Queries data. | +| Subscribing to changes in the distributed data | on(event: 'dataChange', type: SubscribeType, observer: Callback<ChangeNotification>): void
on(event: 'syncComplete', syncCallback: Callback<Array<[string, number]>>): void | Subscribes to data changes in the KV store. | +| Synchronizing data across devices | sync(deviceIdList: string[], mode: SyncMode, allowedDelayMs?: number): void | Triggers database synchronization in manual mode. | + + + + +## How to Develop The following uses a single KV store as an example to describe the development procedure. -1. Import the distributed database module. - - ```js - import distributedData from '@ohos.data.distributedData'; - ``` - -2. Create a **KvManager** instance based on the specified **KvManagerConfig** object. - - 1. Create a **KvManagerConfig** object based on the application context. - 2. Create a **KvManager** instance. - - The sample code is as follows: - - ```js - let kvManager; - try { - const kvManagerConfig = { - bundleName : 'com.example.datamanagertest', - userInfo : { - userId : '0', - userType : distributedData.UserType.SAME_USER_ID - } - } - distributedData.createKVManager(kvManagerConfig, function (err, manager) { - if (err) { - console.log("createKVManager err: " + JSON.stringify(err)); - return; - } - console.log("createKVManager success"); - kvManager = manager; - }); - } catch (e) { - console.log("An unexpected error occurred. Error:" + e); - } - ``` - -3. Create and obtain a single KV store. - - 1. Declare the ID of the single KV store to create. - 2. Create a single KV store. You are advised to disable automatic synchronization \(**autoSync:false**\) and call **sync** if a synchronization is required. - - The sample code is as follows: - - ```js - let kvStore; - try { - const options = { - createIfMissing : true, - encrypt : false, - backup : false, - autoSync : false, - kvStoreType : distributedData.KVStoreType.SINGLE_VERSION, - securityLevel : distributedData.SecurityLevel.S2, - }; - kvManager.getKVStore('storeId', options, function (err, store) { - if (err) { - console.log("getKVStore err: " + JSON.stringify(err)); - return; - } - console.log("getKVStore success"); - kvStore = store; - }); - } catch (e) { - console.log("An unexpected error occurred. Error:" + e); - } - ``` - - >![](../public_sys-resources/icon-note.gif) **NOTE:** - >For data synchronization between networked devices, you are advised to open the distributed database during application startup to obtain the database handle. With this database handle \(**kvStore** in this example\), you can perform operations, such as inserting data into the database, without creating the database repeatedly during the lifecycle of the handle. - -4. Subscribe to changes in the distributed data. - - The following is the sample code for subscribing to the data changes of a single KV store: - - ```js - kvStore.on('dataChange', distributedData.SubscribeType.SUBSCRIBE_TYPE_ALL, function (data) { - console.log("dataChange callback call data: " + JSON.stringify(data)); - }); - ``` - -5. Write data to the single KV store. - - 1. Construct the key and value to be written into the single KV store. - 2. Write key-value pairs into the single KV store. - - The following is the sample code for writing key-value pairs of the string type into the single KV store: - - ```js - const KEY_TEST_STRING_ELEMENT = 'key_test_string'; - const VALUE_TEST_STRING_ELEMENT = 'value-test-string'; - try { - kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, function (err,data) { - if (err != undefined) { - console.log("put err: " + JSON.stringify(err)); - return; - } - console.log("put success"); - }); - }catch (e) { - console.log("An unexpected error occurred. Error:" + e); - } - ``` - -6. Query data in the single KV store. - - 1. Construct the key to be queried from the single KV store. - 2. Query data from the single KV store. - - The following is the sample code for querying data of the string type from the single KV store: - - ```js - const KEY_TEST_STRING_ELEMENT = 'key_test_string'; - const VALUE_TEST_STRING_ELEMENT = 'value-test-string'; - try { - kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, function (err,data) { - if (err != undefined) { - console.log("put err: " + JSON.stringify(err)); - return; - } - console.log("put success"); - kvStore.get(KEY_TEST_STRING_ELEMENT, function (err,data) { - console.log("get success data: " + data); - }); - }); - }catch (e) { - console.log("An unexpected error occurred. Error:" + e); - } - ``` - -7. Synchronize data to other devices. - - Select the devices in the same network and the synchronization mode to synchronize data. - - The following is the sample code for data synchronization in a single KV store. **deviceIds** can be obtained by deviceManager by calling **getTrustedDeviceListSync\(\)**, and **1000** indicates that the maximum delay time is 1000 ms. - - ```js - import deviceManager from '@ohos.distributedHardware.deviceManager'; - - let devManager; - // create deviceManager - deviceManager.createDeviceManager("bundleName", (err, value) => { - if (!err) { - devManager = value; - } - }); - - // get deviceIds - let deviceIds = []; - if (devManager != null) { - var deviceList = devManager.getTrustedDeviceListSync(); - for (var i = 0; i < deviceList.length; i++) { - deviceIds[i] = deviceList[i].deviceId; - } - } - kvStore.sync(deviceIds, distributedData.SyncMode.PUSH_ONLY, 1000); - ``` +1. Import the distributed data module. + ```js + import distributedData from '@ohos.data.distributedData'; + ``` + +2. Create a **KvManager** instance based on the specified **KvManagerConfig** object. + 1. Create a **KvManagerConfig** object based on the application context. + 2. Create a **KvManager** instance. + + The sample code is as follows: + ```js + let kvManager; + try { + const kvManagerConfig = { + bundleName : 'com.example.datamanagertest', + userInfo : { + userId : '0', + userType : distributedData.UserType.SAME_USER_ID + } + } + distributedData.createKVManager(kvManagerConfig, function (err, manager) { + if (err) { + console.log("createKVManager err: " + JSON.stringify(err)); + return; + } + console.log("createKVManager success"); + kvManager = manager; + }); + } catch (e) { + console.log("An unexpected error occurred. Error:" + e); + } + ``` + +3. Create and obtain a single KV store. + 1. Declare the ID of the single KV store to create. + 2. Create a single KV store. You are advised to disable automatic synchronization (**autoSync:false**) and call **sync** when a synchronization is required. + + The sample code is as follows: + ```js + let kvStore; + try { + const options = { + createIfMissing : true, + encrypt : false, + backup : false, + autoSync : false, + kvStoreType : distributedData.KVStoreType.SINGLE_VERSION, + securityLevel : distributedData.SecurityLevel.S0, + }; + kvManager.getKVStore('storeId', options, function (err, store) { + if (err) { + console.log("getKVStore err: " + JSON.stringify(err)); + return; + } + console.log("getKVStore success"); + kvStore = store; + }); + } catch (e) { + console.log("An unexpected error occurred. Error:" + e); + } + ``` + + > ![icon-note.gif](../public_sys-resources/icon-note.gif) **NOTE**
+ > For data synchronization between networked devices, you are advised to open the distributed KV store during application startup to obtain the database handle. With this database handle (**kvStore** in this example), you can perform operations, such as inserting data into the KV store, without creating the KV store repeatedly during the lifecycle of the handle. + +4. Subscribe to changes in the distributed data.
+ The following is the sample code for subscribing to the data changes of a single KV store: + ```js + kvStore.on('dataChange', distributedData.SubscribeType.SUBSCRIBE_TYPE_ALL, function (data) { + console.log("dataChange callback call data: " + JSON.stringify(data)); + }); + ``` + +5. Write data to the single KV store. + 1. Construct the key and value to be written into the single KV store. + 2. Write key-value pairs into the single KV store. + + The following is the sample code for writing key-value pairs of the string type into the single KV store: + + ```js + const KEY_TEST_STRING_ELEMENT = 'key_test_string'; + const VALUE_TEST_STRING_ELEMENT = 'value-test-string'; + try { + kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, function (err,data) { + if (err != undefined) { + console.log("put err: " + JSON.stringify(err)); + return; + } + console.log("put success"); + }); + }catch (e) { + console.log("An unexpected error occurred. Error:" + e); + } + ``` + +6. Query data in the single KV store. + 1. Construct the key to be queried from the single KV store. + 2. Query data from the single KV store. + + The following is the sample code for querying data of the string type from the single KV store: + ```js + const KEY_TEST_STRING_ELEMENT = 'key_test_string'; + const VALUE_TEST_STRING_ELEMENT = 'value-test-string'; + try { + kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, function (err,data) { + if (err != undefined) { + console.log("put err: " + JSON.stringify(err)); + return; + } + console.log("put success"); + kvStore.get(KEY_TEST_STRING_ELEMENT, function (err,data) { + console.log("get success data: " + data); + }); + }); + }catch (e) { + console.log("An unexpected error occurred. Error:" + e); + } + ``` + +7. Synchronize data to other devices.
+ Select the devices in the same network and the synchronization mode to synchronize data. + + The following is the sample code for data synchronization in a single KV store. **deviceIds** can be obtained by deviceManager by calling **getTrustedDeviceListSync()**, and **1000** indicates that the maximum delay time is 1000 ms. + ```js + import deviceManager from '@ohos.distributedHardware.deviceManager'; + + let devManager; + // Create deviceManager. + deviceManager.createDeviceManager("bundleName", (err, value) => { + if (!err) { + devManager = value; + // Obtain deviceIds. + let deviceIds = []; + if (devManager != null) { + var devices = devManager.getTrustedDeviceListSync(); + for (var i = 0; i < devices.length; i++) { + deviceIds[i] = devices[i].deviceId; + } + } + try{ + kvStore.sync(deviceIds, distributedData.SyncMode.PUSH_ONLY, 1000); + }catch (e) { + console.log("An unexpected error occurred. Error:" + e); + } + } + }); + ``` +## Samples +The following samples are provided to help you better understand the distributed data development: +- [`KvStore`: distributed database (eTS) (API8)](https://gitee.com/openharmony/app_samples/tree/master/data/Kvstore) +- [Distributed Database](https://gitee.com/openharmony/codelabs/tree/master/Data/JsDistributedData) diff --git a/en/readme/distributed-file.md b/en/readme/distributed-file.md deleted file mode 100644 index 8c480dd566..0000000000 --- a/en/readme/distributed-file.md +++ /dev/null @@ -1,269 +0,0 @@ -# Distributed File - -## Introduction - -Currently, the Distributed File subsystem provides apps with JavaScript APIs for I/O capabilities, including APIs for managing files and directories, obtaining file information, reading and writing data streams of files, and receiving URIs rather than absolute paths. - -### Architecture - -Currently, the Distributed File subsystem provides only local JavaScript file APIs for apps through the FileIO and File modules. The Distributed File subsystem uses LibN to abstract APIs at the NAPI layer, providing basic capabilities such as the basic type system, memory management, and general programming models for the subsystem. This subsystem depends on the engine layer of the JS application development framework to provide the capability of converting JavaScript APIs into C++ code, depends on the application framework to provide app-related directories, and depends on the GLIBC runtimes to provide I/O capabilities. - -**Figure 1** Distributed File subsystem architecture -![](figures/distributed-file-subsystem-architecture.png "distributed-file-subsystem-architecture") - -## Directory Structure - -``` -foundation/distributeddatamgr/distributedfile -└── interfaces # APIs - └── kits # APIs exposed externally -``` - -## Constraints - -Constraints on local I/O APIs: - -- Only UTF-8/16 encoding is supported. -- The URIs cannot include external storage directories. - -## Usage - -### Available APIs - -Currently, the Distributed File subsystem provides APIs for accessing local files and directories. The following table describes the API types classified by function. - -**Table 1** API types - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

API Type

-

Function

-

Related Module

-

Example API (Class Name.Method Name)

-

Basic file API

-

Creates, modifies, and accesses files, and changes file permissions based on the specified absolute paths or file descriptors.

-

@OHOS.distributedfile.fileio

-

accessSync

-

chownSync

-

chmodSync

-

Basic directory API

-

Reads directories and determines file types based on the specified absolute paths.

-

@OHOS.distributedfile.fileio

-

Dir.openDirSync

-

Basic statistical API

-

Collects basic statistics including the file size, access permission, and modification time based on the specified absolute paths.

-

@OHOS.distributedfile.fileio

-

Stat.statSync

-

Streaming file API

-

Reads and writes data streams of files based on the specified absolute paths or file descriptors.

-

@OHOS.distributedfile.fileio

-

Stream.createStreamSync

-

Stream.fdopenStreamSync

-

Sandbox file API

-

Provides a subset or a combination of the capabilities provided by the basic file, directory, and statistical APIs based on the specified URIs.

-

@system.file

-

move

-

copy

-

list

-
- -The URIs used in sandbox file APIs are classified into three types, as described in the following table. - -**Table 2** URI types - - - - - - - - - - - - - - - - - - - - - - - - -

Directory Type

-

Prefix

-

Access Visibility

-

Description

-

Temporary directory

-

internal://cache/

-

Current app only

-

Readable and writable, and can be cleared at any time. This directory is usually used for temporary downloads or caches.

-

Private directory of an app

-

internal://app/

-

Current app only

-

Deleted when the app is uninstalled.

-

External storage

-

internal://share/

-

All apps

-

Deleted when the app is uninstalled. Other apps with granted permissions can read and write files in this directory.

-
- -### Usage Guidelines - -The I/O APIs provided by the Distributed File subsystem can be classified into the following types based on the programming model: - -- Synchronous programming model - - APIs whose names contain **Sync** are implemented as a synchronous model. When a synchronous API is called, the calling process waits until a value is returned. - - The following example opens a file stream in read-only mode, attempts to read the first 4096 bytes, converts them into a UTF-8-encoded string, and then closes the file stream: - - ``` - import fileio from '@OHOS.distributedfile.fileio'; - - try { - var ss = fileio.Stream.createStreamSync("tmp", "r") - buf = new ArrayBuffer(4096) - ss.readSync(buf) - console.log(String.fromCharCode.apply(null, new Uint8Array(buf))) - ss.closeSync() - } - catch (e) { - console.log(e); - } - ``` - - -- Asynchronous programming model: Promise - - In the **@OHOS.distributedfile.fileio** module, the APIs whose names do not contain **Sync** and to which a callback is not passed as their input parameter are implemented as the Promise asynchronous model. The Promise asynchronous model is one of the OHOS standard asynchronous models. When an asynchronous API using the Promise model is called, the API returns a Promise object while executing the concerned task asynchronously. The Promise object represents the asynchronous operation result. When there is more than one result, the results are returned as properties of the Promise object. - - In the following example, a Promise chain is used to open a file stream in read-only mode, attempt to read the first 4096 bytes of the file, display the length of the content read, and then close the file: - - ``` - import fileio from '@OHOS.distributedfile.fileio'; - - try { - let openedStream - fileio.Stream.createStream("test.txt", "r") - .then(function (ss) { - openedStream = ss; - return ss.read(new ArrayBuffer(4096)) - }) - .then(function (res) { - console.log(res.bytesRead); - console.log(String.fromCharCode.apply(null, new Uint8Array(res.buffer))) - return openedStream.close() - }) - .then(function (undefined) { - console.log("Stream is closed") - }) - .catch(function (e) { - console.log(e) - }) - } catch (e) { - console.log(e) - } - ``` - - -- Asynchronous programming model: Callback - - In the **@OHOS.distributedfile.fileio** module, the APIs whose names do not contain **Sync** and to which a callback is directly passed as their input parameter are implemented as the callback asynchronous model. The callback asynchronous model is also one of the OHOS standard asynchronous models. When an asynchronous API with a callback passed is called, the API executes the concerned task asynchronously and returns the execution result as the input parameters of the registered callback. The first parameter is of the **undefined** or **Error** type, indicating that the execution succeeds or fails, respectively. - - The following example creates a file stream asynchronously, reads the first 4096 bytes of the file asynchronously in the callback invoked when the file stream is created, and then closes the file asynchronously in the callback invoked when the file is read: - - ``` - import fileio from '@OHOS.distributedfile.fileio'; - - try { - fileio.Stream.createStream("./testdir/test_stream.txt", "r", function (err, ss) { - if (!err) { - ss.read(new ArrayBuffer(4096), {}, function (err, buf, readLen) { - if (!err) { - console.log('readLen: ' + readLen) - console.log('data: ' + String.fromCharCode.apply(null, new Uint8Array(buf))) - } else { - console.log('Cannot read from the stream ' + err) - } - ss.close(function (err) { - console.log(`Stream is ${err ? 'not' : ''}closed`) - }); - }) - } else { - console.log('Cannot open the stream ' + err) - } - }) - } catch (e) { - console.log(e) - } - ``` - - -- Asynchronous programming model: Legacy - - All APIs in the **@system.file** module are implemented as the legacy asynchronous model. When calling such an API, you need to implement three callbacks \(including **success**, **fail**, and **complete**\) to be invoked when the execution is successful, fails, or is complete, respectively. If the input parameters are correct, the API calls the **success** or **fail** callback based on whether the asynchronous task is successful after the task execution is complete, and finally calls the **complete** callback. - - The following example asynchronously checks whether the file pointed to by the specified URI exists and provides three callbacks to print the check result: - - ``` - import file from '@system.file' - - file.access({ - uri: 'internal://app/test.txt', - success: function() { - console.log('call access success.'); - }, - fail: function(data, code) { - console.error('call fail callback fail, code: ' + code + ', data: ' + data); - }, - complete: function () { - console.log('call access finally.'); - } - }); - - console.log("file access tested done") - ``` - - -## Repositories Involved - -**Distributed File subsystem** - -distributeddatamgr_distributedfile - diff --git a/en/readme/file-management.md b/en/readme/file-management.md index 7a796d6c0d..ed520cdd52 100644 --- a/en/readme/file-management.md +++ b/en/readme/file-management.md @@ -2,7 +2,7 @@ ## Introduction -The file management subsystem provides a complete file management solution for OpenHarmony. It provides secure and easy file access and comprehensive file management capabilities, including: +The file management subsystem provides a complete file management solution for OpenHarmony. It provides secure and easy-to-use file access and comprehensive file management capabilities, including: - A sandbox to ensure the least privilege as well as application data security - Unified management of user files and streamlined user data access and storage to ensure user data security and purity @@ -18,10 +18,10 @@ The file management subsystem provides the file access framework, file sharing f | Module | Description | | ------------ | ------------------------------------------------------------ | | File access interface| 1. Provides complete JavaScript APIs to implement basic file access capabilities.
2. Provides extension APIs for local files, distributed files, and cloud files.| -| Storage management | 1. Provides the data backup and restoration capability to support system and application data backup and cloning.
2. Provides space management capabilities such as application space clearing and statistics, and quota control.
3. Provides storage management capabilities such as mount operations, external card management, device management, and multi-user management.| -| User files | 1. Provides a sandbox to ensure user data security and purity.
2. Allows access to user data only through **medialibrary**.
3. Provides a unified file management framework.
4. Supports distributed and device-cloud capabilities.| -| Application files | 1. Provides a sandbox to ensure the least privilege as well as application data security.
2. Supports file sharing between apps, across devices, and in groups.
3. Allows applications to access distributed and cloud files as they access local files.| -| Distributed capabilities | 1. Provides basic cross-device access capabilities and supports distributed access using the same account and temporary access using different accounts.
2. Support device-cloud interaction irrespective of the data locations.
3. Supports cross-device hopping, such as application hopping and distributed clipboard.| +| Storage management | 1. Provides data backup and restore to support system and application data backup and cloning.
2. Provides space management capabilities such as application space clearing and statistics, and quota control.
3. Provides storage management capabilities such as mount operations, external card management, device management, and multi-user management.| +| User files | 1. Provides a sandbox to ensure user data security and purity.
2. Allows access to user data only through **mediaLibrary**.
3. Provides a unified file management framework.
4. Supports distributed and device-cloud capabilities.| +| Application files | 1. Provides a sandbox to ensure the least privilege as well as application data security.
2. Supports file sharing between applications, across devices, and in groups.
3. Allows applications to access distributed and cloud files as they access local files.| +| Distributed capabilities | 1. Provides basic cross-device access capabilities and supports distributed access using the same account and temporary access using different accounts.
2. Supports device-cloud interaction irrespective of the data locations.
3. Supports cross-device hopping, such as application hopping and distributed pasteboard.| | Basic file system| 1. Supports local file systems such as ext4, Flash-Friendly File System (F2FS), Extensible File Allocation Table (exFAT), and New Technology File System (NTFS).
2. Supports network file systems such as the distributed file system and Network File System (NFS).
3. Provides tools related to file systems.| -- GitLab