Partager via


Tutoriel : Activez la prise en charge des pièces jointes dans votre application Chat

Le kit de développement logiciel (SDK) Chat fonctionne en toute transparence avec Microsoft Teams dans le contexte d'une réunion. Seul un utilisateur Teams peut envoyer des pièces jointes à un utilisateur Azure Communication Services. Un utilisateur Azure Communication Services ne peut pas envoyer de fichiers joints à un utilisateur Teams. Pour connaître les fonctionnalités actuelles, consultez Conversation d’interopérabilité Teams.

Ajoutez la prise en charge des pièces jointes de fichiers

Le kit SDK Chat fournit la propriété previewUrl pour chaque fichier joint. Plus précisément, previewUrl pointe vers une page web sur SharePoint, où l’utilisateur peut voir le contenu du fichier, le modifier et le télécharger s’il dispose de l’autorisation nécessaire.

Certaines contraintes sont associées à cette fonctionnalité :

  • l'administrateur Teams de l'abonné de l'expéditeur peut imposer des stratégies qui limitent ou désactivent entièrement cette fonctionnalité. Par exemple, l’administrateur Teams peut désactiver certaines autorisations (notamment Anyone), ce qui peut rendre l’URL de fichier joint (previewUrl) inaccessible.

  • Nous ne prenons en charge que deux autorisations de fichiers :

    • Anyone
    • People you choose (avec l’adresse e-mail)

    Informez vos utilisateurs Teams qu’aucune des autres autorisations (par exemple People in your organization) n’est prise en charge. Les utilisateurs Teams doivent vérifier que l’autorisation par défaut est prise en charge, une fois qu’ils ont chargé le fichier sur leur client Teams.

  • L'URL de téléchargement direct (url) n'est pas prise en charge.

En plus des fichiers classiques (avec le AttachmentType file), le kit SDK Chat fournit également la propriété AttachmentType image. Les utilisateurs Azure Communication Services peuvent joindre des images d’une manière qui ressemble au comportement du client Microsoft Teams, quand il convertit les images jointes en images incluses au niveau de la couche d’IU. Pour plus d'informations, consultez Gérer les pièces jointes d'image.

Les utilisateurs Azure Communication Services peuvent ajouter des images via l’option Charger à partir de cet appareil, qui s’affiche côté Teams, et le kit SDK Chat retourne ces pièces jointes en tant que image. Pour les images chargées via Joindre des fichiers cloud, les images sont traitées comme des fichiers classiques côté Teams. Ainsi, le kit SDK Chat retourne les pièces jointes en tant que file.

Notez également que les utilisateurs Azure Communication Services peuvent uniquement charger des fichiers par glisser-déplacer ou via les commandes de menu relatives aux pièces jointes, Charger à partir de cet appareil et Joindre des fichiers cloud. Certains types de messages avec des médias incorporés (notamment des clips vidéo, des messages audio et des cartes météorologiques) ne sont actuellement pas pris en charge.

Ce tutoriel explique comment activer la prise en charge des fichiers joints à l’aide du kit SDK Azure Communication Services Chat pour JavaScript.

Exemple de code

Vous trouverez le code finalisé de ce tutoriel sur GitHub.

Prérequis

Objectifs

  • Afficher la pièce jointe dans le thread du message. Chaque carte de fichier joint possède un bouton Ouvrir.
  • Afficher les pièces jointes sous forme d'images incluses.

Gérez les pièces jointes de fichiers

Le kit SDK Chat pour JavaScript retourne une propriété ChatAttachmentType file pour les fichiers joints classiques, et image pour les images incluses dans les messages.

export interface ChatMessageReceivedEvent extends BaseChatMessageEvent {
  /**
   * Content of the message.
   */
  message: string;

  /**
   * Chat message attachment.
   */
  attachments?: ChatAttachment[];
  
  ...
}

export interface ChatAttachment {
  /** Id of the attachment */
  id: string;
  /** The type of attachment. */
  attachmentType: AttachmentType;
  /** The name of the attachment content. */
  name?: string;
  /** The URL that is used to provide the original size of the inline images */
  url?: string;
  /** The URL that provides the preview of the attachment */
  previewUrl?: string;
}

/** Type of supported attachments. */
export type ChatAttachmentType = "image" | "file" | "unknown";

Par exemple, le JSON suivant montre ce à quoi ChatAttachment pourrait ressembler pour une pièce jointe image et une pièce jointe fichier :

"attachments": [
    {
        "id": "08a182fe-0b29-443e-8d7f-8896bc1908a2",
        "attachmentType": "file",
        "name": "business report.pdf",
        "previewUrl": "https://contoso.sharepoint.com/:u:/g/user/h8jTwB0Zl1AY"
    },
    {
        "id": "9d89acb2-c4e4-4cab-b94a-7c12a61afe30",
        "attachmentType": "image", 
        "name": "Screenshot.png",
        "url": "https://contoso.communication.azure.com/chat/threads/19:9d89acb29d89acb2@thread.v2/messages/123/images/9d89acb2-c4e4-4cab-b94a-7c12a61afe30/views/original?api-version=2023-11-15-preview",
        "previewUrl": "https://contoso.communication.azure.com/chat/threads/19:9d89acb29d89acb2@thread.v2/messages/123/images/9d89acb2-c4e4-4cab-b94a-7c12a61afe30/views/small?api-version=2023-11-15-preview"
      }
]

Revenons à présent au gestionnaire d’événements que vous avez créé dans Démarrage rapide : Joindre votre application de conversation à une réunion Teams, et ajoutons une logique supplémentaire pour gérer les pièces jointes avec la propriété attachmentType file :

chatClient.on("chatMessageReceived", (e) => {
  console.log("Notification chatMessageReceived!");
  // Check whether the notification is intended for the current thread
  if (threadIdInput.value != e.threadId) {
     return;
  }
   
  if (e.sender.communicationUserId != userId) {
    renderReceivedMessage(e);
  } else {
    renderSentMessage(e.message);
  }
});

async function renderReceivedMessage(event) {
    messages += `<div class="container lighter"> ${event.message} </div>`;
    messagesContainer.innerHTML = messages;

    // Get the list of attachments and calls renderFileAttachments to construct a file attachment card
    var attachmentHtml = event.attachments
        .filter(attachment => attachment.attachmentType === "file")
        .map(attachment => renderFileAttachments(attachment))
        .join('');
    messagesContainer.innerHTML += attachmentHtml;
}

function renderFileAttachments(attachment) {
    var re = /(?:\.([^.]+))?$/;
    var fileExtension = re.exec(attachment.name)[1];  
    return '<div class="attachment-container">' +
        '<img class="attachment-icon" alt="attachment file icon" />' +
        '<div>' +
        '<p class="attachment-type">' + fileExtension + '</p>' +
        '<p>' + attachment.name + '</p>' +
        '<a href=' + attachment.previewUrl + ' target="_blank" rel="noreferrer">Open</a>' +
        '</div>' +
        '</div>';
}

Veillez à ajouter du code CSS pour la carte de pièce jointe :

  /* Let's make the chat popup scrollable */
  .chat-popup {

     ...

     max-height: 650px;
     overflow-y: scroll;
}

 .attachment-container {
     overflow: hidden;
     background: #f3f2f1;
     padding: 20px;
     margin: 0;
     border-radius: 10px;
}
 .attachment-container img {
     width: 50px;
     height: 50px;
     float: left;
     margin: 0;
}
 .attachment-container p {
     font-weight: 700;
     margin: 0 5px 20px 0;
}
 .attachment-container {
     display: grid;
     grid-template-columns: 100px 1fr;
     margin-top: 5px;
}
 .attachment-icon {
     content: url("data:image/svg+xml;base64, ...");
}
 .attachment-container a {
     background-color: #dadada;
     color: black;
     font-size: 12px;
     padding: 10px;
     border: none;
     cursor: pointer;
     border-radius: 5px;
     text-align: center;
     margin-right: 10px;
     text-decoration: none;
     margin-top: 10px;
}
 .attachment-container a:hover {
     background-color: black;
     color: white;
}
 .attachment-type {
     position: absolute;
     color: black;
     border: 2px solid black;
     background-color: white;
     margin-top: 50px;
     font-family: sans-serif;
     font-weight: 400;
     padding: 2px;
     text-transform: uppercase;
     font-size: 8px;
}

C’est tout ce dont vous avez besoin pour gérer les fichiers joints. Exécutons ensuite le code.

Exécuter le code

Pour Webpack, vous pouvez utiliser la propriété webpack-dev-server afin de générer et d’exécuter votre application. Exécutez la commande suivante pour préparer un pack de votre hôte d’application sur un serveur web local :

npx webpack-dev-server --entry ./client.js --output bundle.js --debug --devtool inline-source-map

Ou :

npm start

Démonstration de pièce jointe de fichier

  1. Ouvrez votre navigateur et accédez àhttp://localhost:8080/. Saisissez l’URL de la réunion et l’ID du thread.

  2. Envoyez des fichiers joints à partir du client Teams.

    Capture d’écran montrant le client Teams avec un message envoyé contenant trois fichiers joints.

  3. Vous devez voir le nouveau message s’afficher avec les fichiers joints.

    Capture d’écran montrant un exemple d’application, qui comporte un message entrant reçu avec trois fichiers joints.

Gérez les pièces jointes d’images

Les pièces jointes d'image doivent être traitées différemment des pièces jointes standard file. Les images jointes ont la propriété attachmentType image, ce qui nécessite l’utilisation du jeton de communication pour récupérer l’aperçu ou les images en taille réelle.

Avant de continuer, suivez le tutoriel montrant comment activer la prise en charge des images incluses dans votre application de conversation. Ce tutoriel explique comment extraire des images qui nécessitent un jeton de communication dans l'en-tête de demande. Une fois que vous avez reçu l’objet blob de type image, vous devez créer une propriété ObjectUrl, qui pointe vers cet objet blob. Vous injectez ensuite cette URL dans l’attribut src de chaque image incluse.

Vous savez à présent comment fonctionnent les images incluses, vous pouvez donc afficher les images jointes en tant qu’images incluses classiques.

Tout d'abord, injectez une balise image dans le contenu du message chaque fois qu'il y a une image en pièce jointe :

async function renderReceivedMessage(event) {
    messages += `<div class="container lighter"> ${event.message} </div>`;
    messagesContainer.innerHTML = messages;
    console.log(event);
    // Filter out inline images from attachments
    const imageAttachments = event.attachments?.filter(
        (attachment) =>
        attachment.attachmentType === "image" && !messages.includes(attachment.id)
    );
    // Inject image tag for all image attachments
    var imageAttachmentHtml =
        imageAttachments
        .map((attachment) => renderImageAttachments(attachment))
        .join("") ?? "";
    messagesContainer.innerHTML += imageAttachmentHtml;

    // Get list of attachments and calls renderFileAttachments to construct a file attachment card
    var attachmentHtml =
        event.attachments
        ?.filter((attachment) => attachment.attachmentType === "file")
        .map((attachment) => renderFileAttachments(attachment))
        .join("") ?? "";
    messagesContainer.innerHTML += attachmentHtml;

    // Fetch and render preview images
    fetchPreviewImages(imageAttachments);
}

function renderImageAttachments(attachment) {
    return `<img alt="image" src="" itemscope="png" id="${attachment.id}" style="max-width: 100px">`
}

À présent, empruntons la fonction fetchPreviewImages() au Tutoriel : Activer la prise en charge des images incluses, et utilisons-la en l’état, sans aucun changement :

function fetchPreviewImages(attachments) {
    if (!attachments.length > 0) {
        return;
    }
    Promise.all(
        attachments.map(async (attachment) => {
            const response = await fetch(attachment.previewUrl, {
                method: 'GET',
                headers: {
                    'Authorization': 'Bearer ' + tokenString,
                },
            });
            return {
                id: attachment.id,
                content: await response.blob(),
            };
        }),
    ).then((result) => {
        result.forEach((imageRef) => {
            const urlCreator = window.URL || window.webkitURL;
            const url = urlCreator.createObjectURL(imageRef.content);
            document.getElementById(imageRef.id).src = url;
        });
    }).catch((e) => {
        console.log('error fetching preview images');
    });
}

Cette fonction a besoin d’une propriété tokenString. Vous devez donc disposer d’une copie globale initialisée dans init(), comme le montre l’extrait de code suivant :

var tokenString = '';

async function init() {
    ...
    const {
        token,
        expiresOn
    } = tokenResponse;
    
    tokenString = token;
    ...
}

Vous disposez à présent d'un support pour les pièces jointes aux images. Continuez à exécuter le code et à le voir en action.

Démonstration de pièce jointe d’image

  1. Envoyez des images jointes à partir du client Teams.

    Capture d’écran du client Teams montrant une zone d’envoi avec une image jointe chargée.

  2. Une fois que vous avez envoyé l’image jointe, notez qu’elle devient une image incluse du côté du client Teams.

    Capture d’écran du client Teams montrant un message avec l’image jointe envoyée à l’autre participant.

  3. Retournez à l’exemple d’application, puis vérifiez que la même image est affichée.

    Capture d’écran d’un exemple d’application montrant un message entrant avec une image incluse affichée.

Ce tutoriel explique comment activer la prise en charge des fichiers joints à l’aide du kit SDK Azure Communication Services Chat pour C#.

Dans ce tutoriel, vous allez apprendre à :

  • Gérer les fichiers joints.
  • Gérer les images jointes.

Prérequis

Exemple de code

Recherchez le code finalisé pour ce tutoriel sur GitHub.

Gérez les pièces jointes de fichiers

Le kit SDK Chat pour C# retourne une propriété ChatAttachmentType file pour les fichiers joints classiques, et image pour les images incluses.

public readonly partial struct ChatAttachmentType : IEquatable<ChatAttachmentType>
{
        private const string ImageValue = "image";
        private const string FileValue = "file";
        /// <summary> image. </summary>
        public static ChatAttachmentType Image { get; } = new ChatAttachmentType(ImageValue);
        /// <summary> file. </summary>
        public static ChatAttachmentType File { get; } = new ChatAttachmentType(FileValue);
}


Par exemple, le code JSON suivant montre à quoi peut ressembler ChatAttachment pour une image jointe et un fichier joint quand vous recevez des requêtes côté serveur :

"attachments": [
    {
        "id": "08a182fe-0b29-443e-8d7f-8896bc1908a2",
        "attachmentType": "file",
        "name": "business report.pdf",
        "previewUrl": "https://contoso.sharepoint.com/:u:/g/user/h8jTwB0Zl1AY"
    },
    {
        "id": "9d89acb2-c4e4-4cab-b94a-7c12a61afe30",
        "attachmentType": "image", 
        "name": "Screenshot.png",
        "url": "https://contoso.communication.azure.com/chat/threads/19:9d89acb29d89acb2@thread.v2/messages/123/images/9d89acb2-c4e4-4cab-b94a-7c12a61afe30/views/original?api-version=2023-11-15-preview",
        "previewUrl": "https://contoso.communication.azure.com/chat/threads/19:9d89acb29d89acb2@thread.v2/messages/123/images/9d89acb2-c4e4-4cab-b94a-7c12a61afe30/views/small?api-version=2023-11-15-preview"
      }
]

Revenez à présent au gestionnaire d’événements que vous avez créé dans le guide de démarrage rapide précédent, puis ajoutez une logique supplémentaire pour gérer les pièces jointes avec la propriété ChatAttachmentType file :


await foreach (ChatMessage message in allMessages)
{
    // Get message attachments that are of type 'file'
    IEnumerable<ChatAttachment> fileAttachments = message.Content.Attachments.Where(x => x.AttachmentType == ChatAttachmentType.File);
    var chatAttachmentFileUris = new List<Uri>();
    foreach (var file in fileAttachments) 
    {
        chatAttachmentFileUris.Add(file.PreviewUri);
    }

    // Build message list
    if (message.Type == ChatMessageType.Html || message.Type == ChatMessageType.Text)
    {
        textMessages++;
        var userPrefix = message.Sender.Equals(currentUser) ? "[you]:" : "";
        var strippedMessage = StripHtml(message.Content.Message);
      


        var chatAttachments = fileAttachments.Count() > 0 ? "[Attachments]:\n" + string.Join(",\n", chatAttachmentFileUris) : "";
        messageList.Add(long.Parse(message.SequenceId), $"{userPrefix}{strippedMessage}\n{chatAttachments}");
    }
}

Plus précisément, pour chaque fichier joint, vous obtenez la propriété previewUrl, et vous construisez une liste d’URL dans for loop. Vous incorporez ensuite la chaîne dans le contenu du message de conversation.

Gérez les pièces jointes d’images

Vous devez gérer les pièces jointes de type image différemment des pièces jointes file standard. Les images jointes ont la propriété ChatAttachmentType image, ce qui nécessite l’utilisation du jeton de communication pour récupérer l’aperçu ou les images en taille réelle.

Avant de continuer, terminez le tutoriel Activer la prise en charge des images incluses. Pour identifier les images jointes, vous devez déterminer si le contenu du message contient le même ID d’image que les pièces jointes.

bool isImageAttachment = message.Content.Message.Contains(x.Id);

Si cet indicateur a la valeur true, vous appliquez la logique de l’image incluse pour l’afficher :

IEnumerable<ChatAttachment> imageAttachments = message.Content.Attachments.Where(x => x.AttachmentType == ChatAttachmentType.Image);
// Fetch image and render
var chatAttachmentImageUris = new List<Uri>();
foreach (ChatAttachment imageAttachment in imageAttachments)
{
    client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", communicationTokenCredential.GetToken().Token);
    var response = await client.GetAsync(imageAttachment.PreviewUri);
    var randomAccessStream = await response.Content.ReadAsStreamAsync();
    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
    {
        var bitmapImage = new BitmapImage();
        await bitmapImage.SetSourceAsync(randomAccessStream.AsRandomAccessStream());
        InlineImage.Source = bitmapImage;
    });
    chatAttachmentImageUris.Add(imageAttachment.PreviewUri);
}

Votre application prend désormais en charge les pièces jointes d'images.

Étapes suivantes