import { Tabs, TabItem } from '@/components/Tabs';

# Working with Objects in CloudDisk

An object consists of data (like a file) and its associated metadata.

It consists of a unique "key" and content.

Unlike traditional storage, CloudDisk does not have folders. When displayed, the object key name is presented as a folder structure using prefixes and delimiters. Unlike traditional storage, CloudDisk **does not support** moving or renaming directories.

## Naming Objects

The object `key` uniquely identifies the object in a bucket. When you create an object, you specify the key name. The object key name is a sequence of Unicode characters encoded in UTF-8, with a maximum length of 1,024 bytes. Object key names are **case sensitive**.

Objects are stored in a flat structure: a bucket holds objects directly, with no nested buckets or real subfolders. A folder hierarchy is only implied, by splitting the key on a delimiter — this is exactly how the CloudDisk console presents objects as folders.

Since `key` is the only way to retrieve and search for objects, we recommend that you add some key information to the `key`, such as business line, function type, time, search criteria, etc. For example:

- `policy/2024/05/pid-xxxxx/abc.pdf`
- `log/your-bff/2024/console.log`
- `images/Asia/Singapore/Merlion.png`

We recommend that `key` should not start with `/` and cannot use `soap`.

A key may contain any UTF-8 character, but not every character travels well through DNS, URLs, XML parsers and downstream APIs. Keeping to the character set below avoids most of these problems.

Characters that are safe to use in a key:

- Alphanumeric characters: *a-z*, *A-Z*, *0-9*
- Hyphen: `-`
- Underscore: `_`
- Period: `.`
- Forward slash: `/`

The following characters are usable, but your code has to URL-encode them (or write them as HEX) wherever the key appears in a URL:

`!`, `*`, `'`, `(`, `)`,`$`,`@`,`=`,`;`,`:`,`+`,`,`,`?`

Avoid any other character, including non-printable ones. Their handling differs from one application and browser to the next, so keys that contain them are hard to use reliably.

## Put Objects (Upload Objects)

A bucket accepts any type of file — images, backups, exports, media, and so on. A single *Put Object* request works best for objects under 50 MB; beyond that size, use [multipart upload](#multipart-upload) instead. For the hard limit on a single request, see [Quota Standards](../InsureMO_Foundation_Quota_Standards.md).

<Tabs>
<TabItem value="portal" label="Using insureMO Portal (Console)">

Select the folder (prefix) where you want to upload the file, or enter prefix to enter the folder where you want to upload the file. Click **Upload** to show *Upload Files* dialog.

![](../image/clouddisk/v3_object_upload_1.png)

Click and select file or drag file to the area for upload. You can upload five files at a time.

![](../image/clouddisk/v3_object_upload_2.png)

</TabItem>
<TabItem value="api" label="Using insureMO API">

For more CloudDisk API list, see [CloudDisk API](https://docs.insuremo.com/tech/pro/sdk/api/clouddisk)

Call *Put Object* to add an object.

> HTTP Method

POST

> Request URL

{`{{insuremo_gw_url}}/mo-fo/1.0/clouddisk/v3/object/put?key={key}&bucket={bucket}`}

> Query Parameters

- `bucket`: *example-bucket-name*. Required. The bucket name to which the PUT action was initiated.
- `key`: *example/key/to.txt*. Required. Object key for which the PUT action was initiated. It should be url-formatted.

> Header Parameters

Except for `Authorization`, all other heads are optional.

- `Authorization`: *Bearer MOATYourAccessToken*. Required.
- `Cache-Control`: Can be used to specify caching behavior along the request/reply chain. For more information, see [rfc2616-sec14](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9). 
- `Content-Disposition`: Specifies presentational information for the object. For more information, see [rfc6266-section-4](https://www.rfc-editor.org/rfc/rfc6266#section-4).
- `Content-Encoding`: Specifies what content encodings have been applied to the object and thus what decoding mechanisms must be applied to obtain the media-type referenced by the Content-Type header field. For more information, see [rfc9110-content-encoding](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-encoding).
- `Content-Language`: The language which the content is in.
- `Content-Length`: Size of the body in bytes. This parameter is useful when the size of the body cannot be determined automatically. For more information, see [rfc9110-content-length](https://www.rfc-editor.org/rfc/rfc9110.html#name-content-length).
- `Content-Type`: A standard MIME type describing the format of the contents. For more information, see [rfc9110-content-type](https://www.rfc-editor.org/rfc/rfc9110.html#name-content-type).
- `X-Mo-Meta-Xxx`: Specifies user-defined metadata. *Xxx* is the key of the metadata.
- `X-Mo-Tag`: The tag-set for the object. The tag-set must be encoded as URL Query parameters. (For example, "key1=value1")

> Request Body:

*...... binary of object data ......*

> Example Response Body

```json
{
    "code": "i_common_success",
    "message": "request success",
    "trace_id": "traceid0000000000000000000000000",
    "data": {
        "etag": "\"abcdabcd1234123400oo00oo\"",
        "version_id": "abcdabcd1234123400oo00oo"
    }
}
```

</TabItem>
<TabItem value="sdk" label="Using insureMO SDK for JAVA">

```java
import com.insuremo.sdk.core.client.SDKClient;
import com.insuremo.sdk.core.exception.ApiException;
import com.insuremo.sdk.core.model.MOInputStream;
import com.insuremo.sdk.services.clouddisk.ClouddiskSdkClient;
import com.insuremo.sdk.services.clouddisk.bundle.PutObjectResponseBundle;

import java.io.File;
import java.io.IOException;

public class ObjectApi_putObject {

    public static void main(String[] args) {
        System.out.println("----- Example for ObjectApi.putObject -----");
        System.out.println("----- building api -----");

        // spec gateway url, default http://gateway.mo-fo
        String gateway = "https://portal-gw.insuremo.com";
        String accessToken = "your-access-token";

        SDKClient sdkClient = SDKClient.builder().
                defaultConfigs().
                gateway(gateway).
                accessToken(accessToken).
                build();
        ClouddiskSdkClient client = new ClouddiskSdkClient(sdkClient);

        System.out.println("----- putObject -----");

        try {
            File file = new File("/path/to/your/file");
            PutObjectResponseBundle responseBundle = client.objectApi().newPutObjectRequestBuilder().
                    bucket("your-bucket-name").
                    key("example.png").
                    body(MOInputStream.fromFile(file).withContentLength(file.length()).withContentType("image/png")).
                    doRequest();
            System.out.println(responseBundle.getBody());
        } catch (ApiException e) {
            System.out.println("error when call api");
            System.out.println(e.getResponseBody());
            System.out.println(e.getCode());
            throw e;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        System.out.println("----- Done. -----");
    }
}
```

</TabItem>
</Tabs>

### Supported Headers on Upload

Object metadata is a set of name-value pairs that you attach when you upload an object. Metadata is fixed once the object is written — to change it, upload the object again with the new values.

If you need labels that stay editable, use [object tags](https://docs.insuremo.com/tech/pro/sdk/api/clouddisk?id=PutObjectTags) (`X-Mo-Tag`) instead. Unlike metadata, tags can be added, read, and removed on an existing object at any time.

The table below lists the metadata CloudDisk keeps for an object, and whether you can set the value yourself. Not every storage keeps every header; see [Supported Headers by Storage](./InsureMO_clouddisk_bucket.md#supported-headers-by-storage).

| Name                  | Description                                                                                                                                                                     | Can user specify the value? |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- |
| `Date`                | The current date and time.                                                                                                                                                      | No                          |
| `Cache-Control`       | A general header field used to specify caching policies.                                                                                                                        | Yes                         |
| `Content-Disposition` | Object presentational information.                                                                                                                                              | Yes                         |
| `Content-Encoding`    | The content encodings applied to the object, such as `gzip`.                                                                                                                    | Yes                         |
| `Content-Language`    | The language the object content is in.                                                                                                                                          | Yes                         |
| `Content-Length`      | The object size in bytes.                                                                                                                                                       | No                          |
| `Content-Type`        | The object type.                                                                                                                                                                | Yes                         |
| `Last-Modified`       | The object creation date or the last modified date, whichever is later. In the case of multipart uploads, the object creation date is considered to be the date when the multipart upload was initiated. | No                          |
| `ETag`                | An entity tag (ETag) that represents a specific version of an object. For objects that are not uploaded as a multipart upload, the ETag is an MD5 digest of the data.           | No                          |
| `X-Mo-Tag`            | The tag-set for the object. The tag-set must be encoded as URL Query parameters.                                                                                                | Yes                         |
| `X-Mo-Meta-Xxx`       | The user-defined metadata for the object. *Xxx* is the key of metadata.                                                                                                         | Yes                         |

### Multipart Upload

Multipart upload splits one object into several parts, each a contiguous slice of the object's data. Parts are uploaded independently and in any order, so a part that fails in transit can be retried on its own without resending the rest. Consider multipart upload once an object reaches 50 MB.

The flow has three steps: create the upload, upload every part, then complete the upload.

> Create a Multipart Upload

CloudDisk answers the create request with an upload ID that identifies this multipart upload. Pass that upload ID on every subsequent call — uploading a part, completing the upload, or aborting it. Any metadata for the final object has to be supplied here, in the create request.

> Upload Parts

Besides the upload ID, each part needs a part number between 1 and 10,000. The part number fixes where that part sits in the assembled object; the numbers you use do not have to be consecutive (1, 5, and 14 is fine). Reusing a part number overwrites the part previously uploaded under it.

Every part must be at least 5 MB, except the last one, which has no minimum size.

> Complete a Multipart Upload

Your *complete multipart upload* request must include the upload ID and a list of both part numbers and corresponding `ETag` values. This ETag is not necessarily an MD5 hash of the object data.

> Abort a Multipart Upload

Uploaded parts occupy storage and are billed as such until the upload is finished. Always close out a multipart upload — either complete it, or abort it to discard the parts already uploaded.

## Get Objects (Download Objects)

When you download an object, you can get both the content of the object and the metadata of the object. Metadata contains the content specified when the object is uploaded and the system-generated content.

In addition to downloading objects normally, we also provide other ways to get objects:

- Get an object with conditional requests.
- Get part of an object.
- Get an object with pre-signed-url (Share objects).
- Get objects as a ZIP packages(*This is a new feature in the next version, if you are interested or want to contribute, please contact us*).

<Tabs>
<TabItem value="portal" label="Using insureMO Portal (Console)">

To Preview Object Content

Find and select the object, and then click **Preview** on the right side. You can preview text and images on the console. 

![](../image/clouddisk/v3_object_preview_1.png)

To Download an Object

Find and select the object, and then click **Download** on the right side.

![](../image/clouddisk/v3_object_download_1.png)

</TabItem>
<TabItem value="api" label="Using insureMO API">

For more CloudDisk API list, see [CloudDisk API](https://docs.insuremo.com/tech/pro/sdk/api/clouddisk).

Call *Get Object* to get an object.

> HTTP Method

GET

> Request URL

{`{{insuremo_gw_url}}/mo-fo/1.0/clouddisk/v3/object/get?key={key}&bucket={bucket}`}

> Query Parameters

- `bucket`: *example-bucket-name*. Required. The name of the bucket that contains the object.
- `key`: *example/key/to.txt*. Required. Key of the object to get. It should be url-formatted.

> Header Parameters

Except for `Authorization`, all other heads are optional.

- `Authorization`: *Bearer MOATYourAccessToken*. Required.
- `If-Modified-Since`: Return the object only if it has been modified since the specified time; otherwise, return `304 Not Modified` with no body. For more information about conditional requests, see [RFC 7232](https://tools.ietf.org/html/rfc7232).
- `If-None-Match`: Return the object only if its entity tag (ETag) is different from the one specified in this header; otherwise, return `304 Not Modified` with no body. For more information about conditional requests, see [RFC 7232](https://tools.ietf.org/html/rfc7232).
- `Range`: Downloads the specified byte range of an object. For more information about the HTTP Range header, see [RFC 9110 - range](https://www.rfc-editor.org/rfc/rfc9110.html#name-range).

> Response Body

*...... binary of object data ......*

> Response Headers

The response headers will be different depending on the request headers and the headers from `put object` API, and all headers may not return.

- `Accept-Ranges`: Indicates that the object supports range requests, usually `bytes`.
- `Cache-Control`: Specifies caching behavior along the request/reply chain.
- `Content-Disposition`: Specifies presentational information for the object.
- `Content-Encoding`: Indicates what content encodings have been applied to the object and thus what decoding mechanisms must be applied to obtain the media-type referenced by the Content-Type header field.
- `Content-Language`: The language the content is in.
- `Content-Length`: Size of the body in bytes.
- `Content-Range`: The portion of the object returned in the response.
- `Content-Type`: A standard MIME type describes the format of the object data.
- `ETag`: An entity tag (ETag) is an opaque identifier assigned by a web server to a specific version of a resource found at a URL.
- `Last-Modified`: Date and time when the object was last modified.

</TabItem>
<TabItem value="sdk" label="Using insureMO SDK for JAVA">

```java
import com.insuremo.sdk.core.client.ResponseTransformer;
import com.insuremo.sdk.core.client.SDKClient;
import com.insuremo.sdk.core.exception.ApiException;
import com.insuremo.sdk.services.clouddisk.ClouddiskSdkClient;
import com.insuremo.sdk.services.clouddisk.bundle.GetObjectResponseBundle;

import java.nio.charset.StandardCharsets;

public class ObjectApi_getObject {

    public static void main(String[] args) {
        System.out.println("----- Example for ObjectApi.getObject -----");
        System.out.println("----- building api -----");

        // spec gateway url, default http://gateway.mo-fo
        String gateway = "https://portal-gw.insuremo.com";
        String accessToken = "your-access-token";

        SDKClient sdkClient = SDKClient.builder().
                defaultConfigs().
                gateway(gateway).
                accessToken(accessToken).
                build();
        ClouddiskSdkClient client = new ClouddiskSdkClient(sdkClient);

        System.out.println("----- getObject -----");

        try {
            GetObjectResponseBundle<String> responseBundle = client.objectApi().newGetObjectRequestBuilder().
                    bucket("your-bucket-name").
                    key("your/object/key").
                    doRequest(ResponseTransformer.toStringData(StandardCharsets.UTF_8));
            System.out.println(new String(responseBundle.getBody()));
            System.out.println(responseBundle.getAllHeaders());
        } catch (ApiException e) {
            System.out.println("error when call api");
            System.out.println(e.getResponseBody());
            System.out.println(e.getCode());
            throw e;
        }

        System.out.println("----- Done. -----");
    }
}
```

</TabItem>
</Tabs>

## List Objects

Prefixes are how you organize objects in a bucket. A prefix is just the leading part of a key shared by a group of objects — it reads like a directory name and groups related data together, but no directory actually exists behind it.

<Tabs>
<TabItem value="portal" label="Using insureMO Portal (Console)">

### To Show Objects

Click **Objects** on the right side, and then it will open the Object Management page.

![](../image/clouddisk/v3_bucket_objects_1.png)

To manage your objects, you should choose a bucket first. You can see objects as the directory structure in the console with "/" as the delimiter. 

To search objects with prefix, you can input the prefix in the search box. 

Only 200 objects will be loaded at a time, so click **Load More** if you need to browse more objects. However, we recommend limiting the maximum number of objects to 200 within the same delimiter.

![](../image/clouddisk/v3_object_list_1.png)

</TabItem>
<TabItem value="api" label="Using insureMO API">

For more CloudDisk API list, see [CloudDisk API](https://docs.insuremo.com/tech/pro/sdk/api/clouddisk).

Call *List Objects* to list objects in a bucket.

> HTTP Method

GET

> Request URL

{`{{insuremo_gw_url}}/mo-fo/1.0/clouddisk/v3/object/list?bucket={bucket}&prefix={prefix}&continuation_token={continuation_token}&delimiter={delimiter}&max_keys={max_keys}&start_after={start_after}`}

> Query Parameters

- `bucket`: *example-bucket-name*. Required. The name of the bucket to list.
- `prefix`: Limits the response to keys that begin with the specified prefix.
- `continuation_token`: You can use this `continuation_token` for pagination of the list results.
- `delimiter`: A delimiter is a character that you use to group keys. 
- `max_keys`: Sets the maximum number of keys returned in the response.
- `start_after`: Where you want to start listing from. It can be any key in the bucket.

> Header Parameters

- `Authorization`: *Bearer MOATYourAccessToken*. Required.

> Example Response Body

```json
{
    "code": "i_common_success",
    "message": "request success",
    "trace_id": "traceid0000000000000000000000000",
    "data": {
        "common_prefixes": [
            "insuremo/",
            "my-prefix/"
        ],
        "contents": [
            {
                "etag": "\"72a54b5c2f7a4683942d62de4e0fe2fb\"",
                "key": "image.jpeg",
                "last_modified": "2024-09-10T09:55:52.087Z",
                "size": 101764
            },
            {
                "etag": "\"eddc02b200ae8a15a7e6b44ac05bf5f1\"",
                "key": "test.txt",
                "last_modified": "2024-05-31T04:16:09.752Z",
                "size": 9
            }
        ],
        "is_truncated": false,
        "key_count": 4
    }
}
```

</TabItem>
<TabItem value="sdk" label="Using insureMO SDK for JAVA">

```java
import com.insuremo.sdk.core.client.SDKClient;
import com.insuremo.sdk.core.exception.ApiException;
import com.insuremo.sdk.services.clouddisk.ClouddiskSdkClient;
import com.insuremo.sdk.services.clouddisk.bundle.ListObjectsResponseBundle;

public class ObjectApi_listObjects {

    public static void main(String[] args) {
        System.out.println("----- Example for ObjectApi.listObjects -----");
        System.out.println("----- building api -----");

        // spec gateway url, default http://gateway.mo-fo
        String gateway = "https://portal-gw.insuremo.com";
        String accessToken = "your-access-token";

        SDKClient sdkClient = SDKClient.builder().
                defaultConfigs().
                gateway(gateway).
                accessToken(accessToken).
                build();
        ClouddiskSdkClient client = new ClouddiskSdkClient(sdkClient);

        System.out.println("----- listObjects -----");

        try {
            ListObjectsResponseBundle responseBundle = client.objectApi().newListObjectsRequestBuilder().
                    bucket("your-bucket-name").
                    prefix("your/prefix/").
                    delimiter("/").
                    doRequest();
            System.out.println(responseBundle.getBody());
        } catch (ApiException e) {
            System.out.println("error when call api");
            System.out.println(e.getResponseBody());
            System.out.println(e.getCode());
            throw e;
        }

        System.out.println("----- Done. -----");
    }
}
```

</TabItem>
</Tabs>

## Share an Object

<Tabs>
<TabItem value="portal" label="Using insureMO Portal (Console)">

Find and select the object, and then click **Share** on the right side.

Click **Generate** on the dialog, and then it will show links. You can copy it and open on the other window.

![](../image/clouddisk/v3_object_share_2.png)

</TabItem>
<TabItem value="api" label="Using insureMO API">

Call *Presign Object* to pre-sign or share an object.

> HTTP Method

POST

> Request URL

{`{{insuremo_gw_url}}/mo-fo/1.0/clouddisk/v3/object/presign?key={key}&bucket={bucket}`}

> Query Parameters

- `bucket`: *example-bucket-name*. Required. The name of the bucket that contains the object.
- `key`: *example/key/to.txt*. Required. Key of the object to share. It should be url-formatted.

> Header Parameters

- `Authorization`: *Bearer MOATYourAccessToken*. Required.

> Request Body

```json
{
  "expired_in_second": 900
}
```

- `expired_in_second`: Optional. How long the URL stays valid, in seconds. Default `900` (15 minutes), maximum `604800` (7 days). A larger value returns the error code `e_clouddisk_object_presign_invalid_expires`. See [Pre-signed URL Errors](./InsureMO_clouddisk_error_codes#pre-signed-url-errors) for other errors about pre-signed URLs.

The response contains one URL per link type. `insuremo` is always returned; a native link of the storage (such as `s3`) is returned only when the storage supports it, see [Supported Actions by Storage](./InsureMO_clouddisk_bucket.md#supported-actions-by-storage).

> Example Response Body

```json
{
    "code": "i_common_success",
    "message": "request success",
    "trace_id": "traceid0000000000000000000000000",
    "data": {
        "urls": {
            "insuremo": "https://xxx.insuremo.com/api/mo-fo/1.0/clouddisk/v3/object/get/public?key=example/key/to.txt&bucket=example-bucket-name&version=&algorithm=insuremo-hmac-v1.1&tenant=insuremo&target_tenant=&date=20241001T000000Z&expires=900&http_method=GET&signature=abcdabcd1234123401010100101010",
            "s3": "https://example-bucket-name.s3.region.amazonaws.com/example/key/to.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AXXXXXXXXXXXXXXXXXXXXXXXX%2F20241001%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20241001T000000Z&X-Amz-Expires=900&X-Amz-SignedHeaders=host&x-id=GetObject&X-Amz-Signature=abcdabcd1234123401010100101010"
        }
    }
}
```

</TabItem>
<TabItem value="sdk" label="Using insureMO SDK for JAVA">

```java
package com.example.clouddisk.single;

import com.insuremo.sdk.core.client.SDKClient;
import com.insuremo.sdk.core.exception.ApiException;
import com.insuremo.sdk.services.clouddisk.ClouddiskSdkClient;
import com.insuremo.sdk.services.clouddisk.bundle.PresignObjectResponseBundle;
import com.insuremo.sdk.services.clouddisk.model.PresignObjectRequest;

public class ObjectApi_presignObject {

    public static void main(String[] args) {
        System.out.println("----- Example for ObjectApi.presignObject -----");
        System.out.println("----- building api -----");

        // spec gateway url, default http://gateway.mo-fo
        String gateway = "https://portal-gw.insuremo.com";
        String accessToken = "your-access-token";

        SDKClient sdkClient = SDKClient.builder().
                defaultConfigs().
                gateway(gateway).
                accessToken(accessToken).
                build();
        ClouddiskSdkClient client = new ClouddiskSdkClient(sdkClient);

        System.out.println("----- presignObject -----");

        try {
            PresignObjectResponseBundle responseBundle = client.objectApi().newPresignObjectRequestBuilder().
                    bucket("your-bucket-name").
                    key("path/to/object/key").
                    body(new PresignObjectRequest().
                            expiredInSecond(900L)
                    ).
                    doRequest();
            System.out.println(responseBundle.getBody());
        } catch (ApiException e) {
            System.out.println("error when call api");
            System.out.println(e.getResponseBody());
            System.out.println(e.getCode());
            throw e;
        }

        System.out.println("----- Done. -----");
    }
}
```

</TabItem>
</Tabs>

## Delete Objects

<Tabs>
<TabItem value="portal" label="Using insureMO Portal (Console)">

Find and select the object, and then click **Delete** on the right side.

![](../image/clouddisk/v3_object_delete_1.png)

</TabItem>
</Tabs>
