# Stockage Objet - Didacticiels

## Premiers pas

### Création d'un Object Store

Connectez-vous à ITCare et accéder à la section **Stockage** via le menu latéral gauche.

Cliquez sur **Créer un espace de stockage***.*

<figure><img src="https://1991151216-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fu3cmMjeBxFoEweG69ePZ%2Fuploads%2Fgit-blob-03677ab981c5c2bb2bdb391a763627338c75f791%2Fimage2022-11-9_13-52-4.jpg?alt=media" alt=""><figcaption></figcaption></figure>

Sélectionnez le **Centre de données** qui sera le site primaire de votre **Object Store** :

<figure><img src="https://1991151216-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fu3cmMjeBxFoEweG69ePZ%2Fuploads%2Fgit-blob-4c26ac8c32676c67889aa85569cd0a40f355d056%2Fimage2022-11-7_8-56-12.png?alt=media" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Lorsque la **géo-réplication** est **activée**, les Objects sont disponibles depuis les 2 URL API du service de Stockage Objet. Voir [#objectstoragearchitecture-authentification](https://academy.cegedim.cloud/francais/stockage/stockage-objet-architecture#objectstoragearchitecture-authentification "mention").
{% endhint %}

{% hint style="danger" %}
La **géo-réplication** ne peut pas être **activée** ou **désactivée** une fois que l'**Object Store** est créé.
{% endhint %}

Recherchez et sélectionnez un **Service** :

<figure><img src="https://1991151216-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fu3cmMjeBxFoEweG69ePZ%2Fuploads%2Fgit-blob-05b4a07694bad1ab953df229999a7111c74554ef%2Fimage2022-11-9_14-19-59.jpg?alt=media" alt=""><figcaption></figcaption></figure>

Entrez un **nom** pour votre Object Store (voir [#limitations-et-bonnes-pratiques](#limitations-et-bonnes-pratiques "mention")).

Vous pouvez également définir un ***Quota***. Le quota peut être modifié à tout moment après la création de l'Object Store.

<figure><img src="https://1991151216-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fu3cmMjeBxFoEweG69ePZ%2Fuploads%2Fgit-blob-2d55e5fd509e3dbebbeebb105296d687a99cf9fe%2Fimage2022-11-7_8-58-41.png?alt=media" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Le nom de votre **Object Store** sera préfixé par " *cos* " + le nom du "*cloud*" auquel appartient le service global sélectionné :**`cos-<cloud>-<votre nom d'object-store>`**

**Exemple**: `cos-cegedimit-hello`
{% endhint %}

La dernière étape résume votre demande de création d'Object Store.

Vérifiez si les informations sont correctes et cliquez sur le bouton **Submit** pour lancer la création.

<figure><img src="https://1991151216-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fu3cmMjeBxFoEweG69ePZ%2Fuploads%2Fgit-blob-e41d0cc8c918ab6a14882c9491cdb692d583bd08%2Fimage2022-11-10_8-38-6.png?alt=media" alt=""><figcaption></figcaption></figure>

Une fois la création terminée (cela peut prendre quelques minutes), une pop-up apparaît, affichant vos informations d'identification et les endpoints disponibles pour votre **Object Store** :

#### **Références**

* Nom d'utilisateur → votre **access\_key**
* Mot de passe → votre **secret\_key**

{% hint style="warning" %}
Gardez votre **secret\_key** en sécurité, elle ne sera plus affiché.

Vous avez la possibilité de régénérer une **secret\_key** , voir [gerer-des-objects-users](https://academy.cegedim.cloud/francais/stockage/stockage-objet/stockage-objet-didacticiels/gerer-des-objects-users "mention")
{% endhint %}

#### **Endpoints**

* Si vous avez sélectionné un centre de données avec la **géo-Réplication** **activée** (étape 2), vous aurez **deux endpoints**, un pour chaque centre de données.
* Si vous avez sélectionné un centre de données avec la **géo-réplication désactivée** (étape 2), vous n'aurez qu'un seul **endpoints**, correspondant au centre de données sélectionné.

<figure><img src="https://1991151216-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fu3cmMjeBxFoEweG69ePZ%2Fuploads%2Fgit-blob-de082205d72b4cf5ba263f2bc87e851f1d12d3a2%2Fimage2022-11-10_8-45-38.jpg?alt=media" alt=""><figcaption></figcaption></figure>

### Gérer un Object Store

Cette page présente des informations détaillées sur votre Object Store :

* Le service global dont fait partie l'Object Store
* Le centre de données où se trouve l'Object Store
* Taille globale et nombre d'objets
* Statut du quotas
* La liste des Object Users

Vous avez également la possibilité de gérer :

* Le quota
* Les Object Users
* Supprimer l'Object Store

<figure><img src="https://1991151216-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fu3cmMjeBxFoEweG69ePZ%2Fuploads%2Fgit-blob-b91b4e9b242de782d021297b9dbff33691866814%2Fimage2022-11-10_11-20-1.jpg?alt=media" alt=""><figcaption></figcaption></figure>

### Créer un Bucket

Vous avez créé votre **Object Store**, il est temps maintenant de créer un **Bucket.**

{% hint style="info" %}
Nous utilisons les utilitaires **aws s3** et **aws s3api** issues du client S3 **AWSCLIv2** sur Linux.`${S3_ENDPOINT}` & `${S3_PROFILE}` sont des variables d'environnement.
{% endhint %}

Utilisez la commande `mb` pour créer un **Bucket** :

```bash
aws --endpoint-url=${S3_ENDPOINT} --profile=${S3_PROFILE} s3 mb s3://my-bucket

# résultat
make_bucket: my-bucket
```

Lister les Buckets :

```bash
# Lister les buckets
aws --endpoint-url=${S3_ENDPOINT} --profile=${S3_PROFILE} s3 ls
 
# Résultat
2022-11-13 11:43:54 my-bucket
```

### Uploader un Objet

Nous avons maintenant un **Bucket**, nous allons y charger des objets.

{% hint style="info" %}
Nous utilisons les utilitaires **aws s3** et **aws s3api** issues du client S3 **AWSCLIv2** sur Linux.`${S3_ENDPOINT}` & `${S3_PROFILE}` sont des variables d'environnement.
{% endhint %}

Utilisez la commande `cp` pour téléchargez un objet :

```bash
aws --endpoint-url=${S3_ENDPOINT} --profile=${S3_PROFILE} s3 cp feather.ttf s3://my-bucket
# Résultat du chargement: ./feather.ttf to s3://my-bucket/feather.ttf
 
# Lister le contenu du bucket
aws --endpoint-url=${S3_ENDPOINT} --profile=${S3_PROFILE} s3 ls s3://my-bucket
 
# Résultat
2022-11-13 11:47:42 81512 feather.ttf
```

Vous pouvez spécifier un **préfixe** lorsque vous téléchargez un objet dans un **Bucket** :

```bash
aws --endpoint-url=${S3_ENDPOINT} --profile=${S3_PROFILE} s3 cp feather.ttf s3://my-bucket/prefix-1/prefix-2/
 
# Résultat
upload: ./feather.ttf to s3://my-bucket/prefix-1/prefix-2/feather.ttf
 
# Lister le contenu du bucket
aws --endpoint-url=${S3_ENDPOINT} --profile=${S3_PROFILE} s3 ls s3://my-bucket/
 
# Résultat
PRE prefix-1/
 
# Lister le contenu du bucket à prefix-1
aws --endpoint-url=${S3_ENDPOINT} --profile=${S3_PROFILE} s3 ls s3://my-bucket/prefix-1/
 
# Résultat
PRE prefix-2/
```

### Gérer le quota d'un Object Store

Vous pouvez spécifier un **quota** sur votre Object Store afin de limiter l'espace utilisé.

{% hint style="warning" %}
Lorsque le quota est atteint, le téléchargement des objets dans un Bucket de l'Object Store **est refusé**.
{% endhint %}

Pour gérer les **quotas**, rendez-vous sur la page d'information détaillée de votre **Object Store** et cliquez sur le bouton **Gestion du quota**.

Vous pouvez définir un **quota** de **1 Go** à **8 To.**

{% hint style="info" %}
Si vous avez besoin de plus de 8 To de **Quota**, veuillez contacter cegedim.cloud.
{% endhint %}

Une fois le **Quota** appliqué, vous pouvez suivre le statut du **Quota** sur la page d'information détaillée de votre Object Store :

<figure><img src="https://1991151216-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fu3cmMjeBxFoEweG69ePZ%2Fuploads%2Fgit-blob-d5e98e638aa1ffbe6adb6fea70abf4a6d2fcad24%2Fimage2022-11-13_12-2-39.png?alt=media" alt=""><figcaption></figcaption></figure>

Lorsque la limite du **Quota** est atteinte, le téléchargement est refusé **(HTTP 403 Forbidden**) :

{% code overflow="wrap" %}

```bash
aws --endpoint-url=${S3_ENDPOINT} --profile=${S3_PROFILE} s3 cp s3://my-bucket/hello/
```

{% endcode %}

{% code title="Résultat" overflow="wrap" %}

```
upload failed: ./feather.ttf to s3://my-bucket/hello/feather.ttf An error occurred (Forbidden) when calling the PutObject operation: Check if quota has been exceeded or object has too many versions
```

{% endcode %}

## Clients recommandés

### S3Browser

**S3Browser** est un logiciel gratuit pour Windows (uniquement). Il offre des fonctionnalités de base avec la version gratuite. Pour les fonctionnalités avancées, il faut acquérir la [version pro](https://s3browser.com/buypro.aspx).

{% embed url="<http://s3browser.com/>" %}

### AWS CLI

**AWS CLI** est l'interface en ligne de commande officielle d'AWS (disponible Windows, Linux et MacOS). Il offre toutes les fonctionnalités et les meilleures performances pour être utilisé avec le service de Stockage Objet de cegedim.cloud.

{% embed url="<https://aws.amazon.com/cli/?nc1=h_ls>" %}

### S5cmd

**s5cmd** est une alternative à **AWS CLI.** Client performant pour des opérations sur l'API S3 et sur les systèmes de fichiers locaux.

{% embed url="<https://github.com/peak/s5cmd>" %}

### Kit de développement logiciel (SDK) <a href="#recommendedclients-kitdedeveloppementlogiciel-sdk" id="recommendedclients-kitdedeveloppementlogiciel-sdk"></a>

Nous recommandons d'utiliser le SDK officiel d'AWS :

* **Java :** <https://aws.amazon.com/documentation/sdkforjava/>
* **.NET :** <https://aws.amazon.com/documentation/sdkfornet/>
* **Node.js**: <https://aws.amazon.com/documentation/sdk-for-javascript/>
* **Python :** <https://boto3.readthedocs.org/en/latest/>

{% embed url="<https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingAWSSDK.html>" %}

## Résilience et PRA <a href="#title-text" id="title-text"></a>

Si vous utilisez un Object Store **géo-répliqué**, vos objets seront disponibles depuis des deux endpoints (voir: [Endpoints](https://docs.cegedim.cloud/display/OST/Endpoints)) du service de de Stockage Objet de cegedim.cloud.

Une bonne pratique consiste à utiliser un client avec une capacité de bascule entre les deux endpoints pour:

* Basculer automatiquement vers un autre endpoints lorsque le première est indisponible.
* Aucune configuration nécessaire pour permettre le basculement de votre application vers un autre endpoints.

<details>

<summary>Pseudo code Java</summary>

{% code lineNumbers="true" %}

```java
/**
  Class responsible to deliver a valid s3 client to other components of application.
  It holds s3 client instances and has ability to test if client is available for each region.
**/
class S3ClientFactory {
    private List<AmazonS3Client> clients;
 
    public void init() {
        // initialization of your clients here
    }
 
    /**
    returns a valid s3 client or throw exception
    **/
    public AmazonS3Client getClient() throws ServiceUnavailableException {
        for (AmazonS3Client client : clients) {
            if (client.isAvailable()) {
                return client;
            }
        }
        throw new ServiceUnavailableException("No S3 client is currently available");
    }
 
}
 
class MyApplication {
     
    S3ClientFactory factory = new S3ClientFactory();
 
    // get a client, whatever the region is
    AmazonS3Client client = factory.getClient();
 
    client.putObject("mybucket", "path/to/object", "One more");
 
}
```

{% endcode %}

</details>

## Vider un Bucket

{% hint style="danger" %}
Vider un **Bucket** est une opération **irréversible**

Les objets ou les **Buckets** supprimés **ne peuvent pas être restaurés**.

L'opération de suppression **peut prendre un certain temps**, en fonction du nombre d'objets et de versions stockés dans le **Bucket**.
{% endhint %}

### Via le client S3

Vous pouvez utiliser n'importe quel client S3, comme [AWS CLI](https://aws.amazon.com/fr/cli/), [s3cmd](https://s3tools.org/s3cmd) ou [s3browser](https://s3browser.com/).

Vous pouvez vider un **Bucket** à l'aide de ce type client uniquement si le versionnage n'est pas activé sur le **Bucket** (voir [Utilisation de la gestion des versions dans un Bucket](https://docs.cegedim.cloud/display/OST/Using+Versioning+Buckets)) **.**

Si le versionnage n'est pas activé, vous pouvez utiliser la commande **rm** (remove) avec l'option `--recursive` pour vider le **Bucket** (ou supprimer un sous-ensemble d'objets avec un préfixe spécifique).

La commande suivante supprime les objets qui ont le préfixe `doc`, `doc/object-1` et `doc/object-2`.

```bash
$ aws s3 rm s3://bucket-name/doc --recursive
```

Utilisez la commande suivante pour supprimer tous les objets sans spécifier de préfixe.

```bash
$ aws s3 rm s3://bucket-name --recursive
```

{% hint style="warning" %}
Vous ne pouvez pas supprimer des objets dans **Bucket** où **le versionnage est activé**.\
S3 ajoute un `marqueur de suppression` (\*\*`DeleteMarker)`\*\*lorsque vous supprimez un objet.

Voir [configuration-de-cycle-de-vie](https://academy.cegedim.cloud/francais/stockage/stockage-objet/stockage-objet-architecture/configuration-de-cycle-de-vie "mention") pour plus d'informations.
{% endhint %}

{% hint style="danger" %}
Vous ne pouvez pas supprimer des objets où **Object Lock** est activé, tant que le délai de rétention défini n'est pas atteint ou que la protection "Legal Hold" n'est pas levée.
{% endhint %}

### Via une configuration de cycle de vie

Si vous utilisez une configuration de **cycle de vie** pour vider un **Bucket**, la configuration de **cycle de vie** doit inclure :

* [current versions](https://docs.aws.amazon.com/AmazonS3/latest/userguide/versioning-workflows.html)
* [non-current versions](https://docs.aws.amazon.com/AmazonS3/latest/userguide/versioning-workflows.html)
* [delete markers](https://docs.aws.amazon.com/AmazonS3/latest/userguide/DeleteMarker.html)
* [incomplete multipart uploads](https://docs.aws.amazon.com/AmazonS3/latest/userguide/mpu-abort-incomplete-mpu-lifecycle-config.html)

Vous pouvez ajouter des règles de configuration du **cycle de vie** pour supprimer tous les objets ou un sous-ensemble d'objets ayant un préfixe spécifique. Par exemple, pour supprimer tous les objets d'un **Bucket**, vous pouvez définir une règle de **cycle de vie** pour que les objets expirent 1 jour après leur création.

Si le versionnage est **activé**, vous pouvez également configurer la règle pour supprimer les version **non courants** de vos objets ([non-current versions](https://docs.aws.amazon.com/AmazonS3/latest/userguide/versioning-workflows.html))

Pour vider complètement un **Bucket** où **le versionnage est activé**, vous devez configurer une configuration de **cycle de vie** pour les objets **courant** ([current versions](https://docs.aws.amazon.com/AmazonS3/latest/userguide/versioning-workflows.html)) **et non courant** ([non-current versions](https://docs.aws.amazon.com/AmazonS3/latest/userguide/versioning-workflows.html)) du **Bucket**.

Vous pouvez ajouter une configuration de **cycle de vie** sur le **Bucket** en utilisant AWS CLI ou un client avec interface graphique comme [s3browser](https://s3browser.com/bucket-lifecycle-configuration.aspx).

{% hint style="info" %}
Pour plus d'informations, voir [configuration-de-cycle-de-vie](https://academy.cegedim.cloud/francais/stockage/stockage-objet/stockage-objet-architecture/configuration-de-cycle-de-vie "mention")
{% endhint %}

Ci-dessous quelques configurations de cycle de vie pour vider Bucket :

<details>

<summary>Bucket sans versionnage</summary>

```json
{
    "Rules": [
        {
            "Expiration": {
                "Days": 1
            },
            "ID": "lifecycle-v2-expire-current-and-mpu",
            "Filter": {
                "Prefix": ""
            },
            "Status": "Enabled",
            "AbortIncompleteMultipartUpload": {
                "DaysAfterInitiation": 1
            }
        }
    ]
}
```

</details>

<details>

<summary>Bucket avec versionnage</summary>

```json
{
    "Rules": [
        {
            "Expiration": {
                "ExpiredObjectDeleteMarker": true
            },
            "ID": "lifecycle-v2-expire-non-current-and-dmarkers-and-mpu",
            "Filter": {
                "Prefix": ""
            },
            "Status": "Enabled",
            "NoncurrentVersionExpiration": {
                "NoncurrentDays": 1
            },
            "AbortIncompleteMultipartUpload": {
                "DaysAfterInitiation": 1
            }
        }
    ]
}
```

</details>

<details>

<summary>Bucket mixte (objets versionnés et non versionnés)</summary>

Ci-dessous un exemple de configuration de **cycle de vie** pour vider un **Bucket**, avec la suppressions des objet courant, non courant, des marqueurs de suppression et les téléchargements "*multipart*" incomplets.

```json
{
    "Rules": [
        {
            "Expiration": {
                "Days": 1
            },
            "ID": "lifecycle-v2-purge-all",
            "Filter": {
                "Prefix": ""
            },
            "Status": "Enabled",
            "NoncurrentVersionExpiration": {
                "NoncurrentDays": 1
            },
            "AbortIncompleteMultipartUpload": {
                "DaysAfterInitiation": 1
            }
        },
        {
            "Expiration": {
                "ExpiredObjectDeleteMarker": true
            },
            "Filter": {
                "Prefix": ""
            },
            "Status": "Enabled"
        }
    ]
}
```

</details>

## Limitations et bonnes pratiques

### Object Stores

#### Limitations

Les règles suivantes s'appliquent au nom des **Object Stores** du service **Stockage Objet** de cegedim.cloud :

* Le nombre de caractères du nom doit être compris entre **1** et **255 caractères.**
* Peut inclure un trait d'union (-) et des caractères alphanumériques (\[a-zA-Z0-9]).
* Évitez l'utilisation de caractères de soulignement (underscore) (\_).
* Évitez l'utilisation des majuscules.
* Ne peut pas commencer par un point (.).
* Ne peut pas contenir un double point (..).
* Ne peut pas se terminer par un point (.).
* Ne peut pas contenir d'espaces.
* Ne doit pas être formaté comme une adresse IPv4.
* Les noms des Object Stores doivent être uniques.

#### Bonnes Pratiques

* Créer un Object Store par projet ou par application.
* La géo-réplication ne peut pas être activée ou désactivée une fois l'Object Store créé.
* Pour de meilleures performances, il est recommandé d'avoir moins de 1000 Buckets dans un seul Object Store.
* Le nom d'un Object Store doivent être compatibles avec le format des URL web.

### Buckets

#### Limitations

Les règles suivantes s'appliquent au nom des **Buckets** du service **Stockage Objet** de cegedim.cloud\*\*:\*\*

* Le nombre de caractères doit être compris entre **3** et **255 caractères**.
* Peut inclure les caractères point (.), tiret (-) et caractères de soulignement (\_) et les caractères alphanumériques (\[a-zA-Z0-9]).
* Évitez l'utilisation des majuscules..
* Peut commencer par un trait d'union (-) ou un caractère alphanumérique.
* Ne peut pas commencer par un point (.).
* Ne peut pas contenir un double point (..).
* Ne peut pas se terminer par un point (.).
* Ne peut pas contenir d'espaces.
* Ne doit pas être formaté comme une adresse IPv4.
* Le nom d'un **Bucket** doivent être uniques au sein d'un **Object Store**.

#### Bonnes Pratiques

* Utilisez les **Buckets** pour un environnement, un flux de travail ou des utilisations spécifiques. Par exemple : dev, test, finance, opérations, etc.
* Dans un **Object Store** dont la **Géo-replication est activée**, créez des **Buckets** en utilisant le endpoint le plus proche (EB4 ou ET1) de l'application qui accède aux objets.

{% hint style="info" %}
Il y a des opérations supplémentaires liés à la vérification de la dernière version d'un objet, si le site interrogé n'est pas le propriétaire de l'objet en question. Cela peut avoir un impact sur les performances.
{% endhint %}

* Pour de meilleures performances, il est recommandé d'avoir moins de 1000 **Buckets** dans un seul **Object Store**.
* Les noms des **Buckets** doivent être **compatibles avec le format des URL web.**

### Objects

#### Limitations

Les règles suivantes s'appliquent à la dénomination des Objets dans la base de données de cegedim.cloud :

* Ne peut pas être null ou une chaîne vide.
* La longueur est comprise entre **1** et **255** (caractères Unicode).
* Évitez d'utiliser l'espace.
* Pas de validation sur les caractères.

#### Bonnes pratiques

* Les noms d'**objets** doivent être **compatibles avec le format des URL web.**

### Petits Objets vs Grands Objets

Cette section fournit des bonnes pratiques pour la manipulation d'objets de petite et de grande taille dans votre application. Il fournit également des informations sur les options de gestion version et les détails sur la compression.

#### Petits Objets <a href="#limitationandbestpractices-petitsobjets" id="limitationandbestpractices-petitsobjets"></a>

Un objet est considéré comme petit lorsqu'il a une taille inférieur à **100 Ko.**

Le service **Stockage Objet** de cegedim.cloud a un mécanisme interne qui améliore la performance pour les écritures de données des objets de faible taille. Il agrège plusieurs objets de mis en file d'attente en mémoire, puis les écrit en une seule opération sur le disque, et cela jusqu'à **2 Mo** de données.

Cela améliore les performances en réduisant le nombre d'allers-retours pour traiter les écritures individuelles au niveau du stockage.

Bien que le service **Stockage Objet** de cegedim.cloud dispose d'optimisations pour les écritures de faible volumétrie, si vous avez la possibilité, dans votre application de définir la taille d'un objet

choisissez alors une taille plus grande (par exemple **1 Mo** plutôt que **64 Ko**) ou une valeur qui s'aligne sur la norme du service **Stockage Objet** de cegedim.cloud de **2 Mo** pour des raisons de performance.

#### Grands Objets <a href="#limitationandbestpractices-grandsobjets" id="limitationandbestpractices-grandsobjets"></a>

L'un des problèmes liés à la lecture et à l'écriture des objets de grande taille est la performance.

Le service **Stockage Objet** de cegedim.cloud fournit certaines fonctionnalités API pour réduire l'impact sur les performances des objets de grande taille, tels que les téléchargements "*multipart*". Voici quelques conseils pour mitiger certains des problèmes liés à l'accès aux objets volumineux :

Lorsque vous travaillez avec des objets volumineux (**> 100 Mo**), utilisez la fonction de téléchargement en plusieurs parties. Cela permet de mettre en pause et de reprendre les téléchargements pour les objets volumineux.

Pour des objet avec un taille inférieur à **1 Go**, la taille du tampon interne étant de **2 Mo**, utilisez un multiple de **2 Mo** (par exemple 8 Mo).

Pour des objets de taille supérieur à **1 Go** utilisez une taille de **128 Mo**.

Le débit des performances peut être amélioré en parallélisant les téléchargements depuis votre application ou client.

Utilisez des API qui permettent un chargement et un téléchargement faciles, par exemple :

* En Java, utilisez le `TransferManager`
* En .NET, utilisez `TransferUtility`
