Partager via


Tutoriel : Activer la prise en charge des images inline dans votre application Conversation

Le Kit de développement logiciel (SDK) Chat est conçu pour fonctionner en toute transparence avec Microsoft Teams. Plus précisément, le Kit de développement logiciel (SDK) Chat fournit une solution permettant de recevoir des images inline et d’envoyer des images inline aux utilisateurs de Microsoft Teams.

Dans ce tutoriel, vous apprendrez comment activer la prise en charge des images en ligne en utilisant le SDK (Kit de développement logiciel) Azure Communication Services Chat pour JavaScript.

Les images incorporées sont des images copiées et collées directement dans la boîte d’envoi du client Teams. Pour les images chargées via le chargement à partir de ce menu d’appareil ou par glisser-déplacer, telles que les images déplacées directement vers la zone d’envoi dans Teams, vous devez faire référence à ce didacticiel dans le cadre de la fonctionnalité de partage de fichiers. (Consultez la section « Gérer les pièces jointes d’image . »)

Pour copier une image, les utilisateurs de Teams ont deux options :

  • Utilisez le menu contextuel de leur système d’exploitation pour copier le fichier image, puis collez-le dans la zone d’envoi de son client Teams.
  • Utilisez des raccourcis clavier.

Dans ce tutoriel, vous allez découvrir ce que vous devez faire lorsque vous procédez comme suit :

Remarque

La possibilité d’envoyer des images inline est actuellement disponible en préversion publique. Il est disponible uniquement pour JavaScript. Pour recevoir des images incorporées, il est actuellement en disponibilité générale. Il est disponible pour JavaScript et C# dans une conversation d’interopérabilité Teams.

Conditions préalables

Exemple de code

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

Gérer les images inline reçues dans un nouvel événement de message

Dans cette section, vous allez apprendre à afficher des images inline incorporées dans le contenu du message d’un nouvel événement reçu.

Dans le guide de démarrage rapide, vous avez créé un gestionnaire d’événements pour l’événement chatMessageReceived , qui est déclenché lorsque vous recevez un nouveau message de l’utilisateur Teams. Vous ajoutez également le contenu du message entrant au messageContainer directement lors de la réception de l’événement chatMessageReceived provenant de chatClient, comme ceci :

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.message);
   }
   else {
      renderSentMessage(e.message);
   }
});
   
async function renderReceivedMessage(message) {
   messages += '<div class="container lighter">' + message + '</div>';
   messagesContainer.innerHTML = messages;
}

À partir de l’événement entrant de type ChatMessageReceivedEvent, une propriété nommée attachments contient des informations sur l’image inline. Il vous suffit d’afficher des images inline dans votre interface utilisateur :

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

  /**
   * Metadata of the message.
   */
  metadata: Record<string, string>;

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

export interface ChatAttachment {
  /** Id of the attachment */
  id: string;
  /** The type of attachment. */
  attachmentType: ChatAttachmentType;
  /** The name of the attachment content. */
  name?: string;
  /** The URL where the attachment can be downloaded */
  url?: string;
  /** The URL where the preview of attachment can be downloaded */
  previewUrl?: string;
}

export type ChatAttachmentType = "image" | "unknown";

Revenez maintenant au code précédent pour ajouter une logique supplémentaire, comme les extraits de code suivants :

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

function renderReceivedMessage(e, isMyMessage) {
  const messageContent = e.message;

  const card = document.createElement('div');
  card.className = isMyMessage ? "container darker" : "container lighter";
  card.innerHTML = messageContent;
  
  messagesContainer.appendChild(card);
  
  // Filter out inline images from attachments
  const imageAttachments = e.attachments.filter((e) =>
    e.attachmentType.toLowerCase() === 'image');
  
  // Fetch and render preview images
  fetchPreviewImages(imageAttachments);
  
  // Set up onclick event handler to fetch full-scale image
  setImgHandler(card, imageAttachments);
}

function setImgHandler(element, imageAttachments) {
  // Do nothing if there are no image attachments
  if (!imageAttachments.length > 0) {
    return;
  }
  const imgs = element.getElementsByTagName('img');
  for (const img of imgs) {
    img.addEventListener('click', (e) => {
      // Fetch full-scale image upon click
      fetchFullScaleImage(e, imageAttachments);
    });
  }
}

async function fetchPreviewImages(attachments) {
  if (!attachments.length > 0) {
    return;
  }
  // Since each message could contain more than one inline image
  // we need to fetch them individually 
  const result = await Promise.all(
      attachments.map(async (attachment) => {
        // Fetch preview image from its 'previewURL'
        const response = await fetch(attachment.previewUrl, {
          method: 'GET',
          headers: {
            // The token here should be the same one from chat initialization
            'Authorization': 'Bearer ' + tokenString,
          },
        });
        // The response would be in an image blob, so we can render it directly
        return {
          id: attachment.id,
          content: await response.blob(),
        };
      }),
  );
  result.forEach((imageResult) => {
    const urlCreator = window.URL || window.webkitURL;
    const url = urlCreator.createObjectURL(imageResult.content);
    // Look up the image ID and replace its 'src' with object URL
    document.getElementById(imageResult.id).src = url;
  });
}

Dans cet exemple, vous avez créé deux fonctions d’assistance, fetchPreviewImages et setImgHandler. La première méthode obtient l’image d’aperçu directement depuis l'objet previewURL fourni dans chaque ChatAttachment en utilisant un en-tête d'authentification. De même, vous configurez un onclick événement pour chaque image dans la fonction setImgHandler. Dans le gestionnaire d’événements, vous récupérez une image à l’échelle complète à partir de la propriété url à partir de l’objet ChatAttachment avec un en-tête d’authentification.

Vous devez maintenant exposer le jeton au niveau global, car vous devez construire un en-tête d’authentification avec celui-ci. Vous devez modifier le code suivant :

// New variable for token string
var tokenString = '';

async function init() {

   ....
   
   let tokenResponse = await identityClient.getToken(identityResponse, [
      "voip",
      "chat"
	]);
	const { token, expiresOn } = tokenResponse;
   
   // Save to token string
   tokenString = token;
   
   ...
}

Pour afficher l’image à grande échelle dans une superposition, vous devez également ajouter un nouveau composant :


<div class="overlay" id="overlay-container">
   <div class="content">
      <img id="full-scale-image" src="" alt="" />
   </div>
</div>

Avec du CSS :


/* let's make chat popup scrollable */
.chat-popup {

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

 .overlay {
    position: fixed; 
    width: 100%; 
    height: 100%;
    background: rgba(0, 0, 0, .7);
    top: 0;
    left: 0;
    z-index: 100;
 }

.overlay .content {
   position: fixed; 
   width: 100%;
   height: 100%;
   text-align: center;
   overflow: hidden;
   z-index: 100;
   margin: auto;
   background-color: rgba(0, 0, 0, .7);
}

.overlay img {
   position: absolute;
   display: block;
   max-height: 90%;
   max-width: 90%;
   top: 50%;
   left: 50%;
   transform: translate(-50%, -50%);
}

#overlay-container {
   display: none
}

Maintenant que vous disposez d’une superposition configurée, il est temps de travailler sur la logique pour afficher des images à grande échelle. Rappelez-vous que vous avez créé un gestionnaire d’événements onClick pour appeler une fonction fetchFullScaleImage:


const overlayContainer = document.getElementById('overlay-container');
const loadingImageOverlay = document.getElementById('full-scale-image');

function fetchFullScaleImage(e, imageAttachments) {
  // Get the image ID from the clicked image element
  const link = imageAttachments.filter((attachment) =>
    attachment.id === e.target.id)[0].url;
  loadingImageOverlay.src = '';
  
  // Fetch the image
  fetch(link, {
    method: 'GET',
    headers: {'Authorization': 'Bearer ' + tokenString},
  }).then(async (result) => {
   
    // Now we set image blob to our overlay element
    const content = await result.blob();
    const urlCreator = window.URL || window.webkitURL;
    const url = urlCreator.createObjectURL(content);
    loadingImageOverlay.src = url;
  });
  // Show overlay
  overlayContainer.style.display = 'block';
}

Une dernière chose que vous souhaitez ajouter est la possibilité de ignorer la superposition lorsque l’image est cliqué :

loadingImageOverlay.addEventListener('click', () => {
  overlayContainer.style.display = 'none';
});

Vous avez maintenant apporté toutes les modifications dont vous avez besoin pour afficher des images inline pour les messages provenant de notifications en temps réel.

Exécuter le code

Les utilisateurs Webpack peuvent utiliser webpack-dev-server pour générer et exécuter votre application. Exécutez la commande suivante pour empaqueter l'hôte de votre application sur un serveur web local :

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

Démonstration

Ouvrez votre navigateur et accédez à http://localhost:8080/. Saisissez l’URL de la réunion et l’ID du thread. Envoyez des images inline à partir du client Teams.

Capture d’écran montrant un client Teams avec un message envoyé qui lit : Voici quelques idées, faites-moi savoir ce que vous pensez ! Le message contient également deux images inline de maquettes intérieures de salle.

Ensuite, vous devez voir le nouveau message affiché avec les images d’aperçu.

Capture d’écran montrant un exemple d’application avec un message entrant avec des images incorporées.

Une fois que l’utilisateur Azure Communication Services sélectionne l’image d’aperçu, une superposition s’affiche avec l’image à grande échelle envoyée par l’utilisateur Teams.

Capture d’écran montrant un exemple d’application avec une superposition d’une image à grande échelle.

Gérer l’envoi d’images inline dans une nouvelle demande de message

Important

Cette fonctionnalité d’Azure Communication Services est actuellement en préversion. Les fonctionnalités en préversion sont disponibles publiquement et peuvent être utilisées par tous les clients Microsoft nouveaux et existants.

Ces interfaces de programmation d’applications et kits de développement logiciel (SDK) en préversion sont fournis sans contrat au niveau du service. Nous vous recommandons de ne pas les utiliser pour les charges de travail de production. Certaines fonctionnalités peuvent ne pas être prises en charge ou les fonctionnalités peuvent être limitées.

Pour plus d’informations, consultez Conditions d’Utilisation Supplémentaires relatives aux Évaluations Microsoft Azure.

Outre la gestion des messages avec des images inline, le Kit de développement logiciel (SDK) Chat pour JavaScript fournit également une solution permettant à l’utilisateur de communication d’envoyer des images inline à l’utilisateur Microsoft Teams dans une conversation d’interopérabilité.

Examinez la nouvelle API à partir de ChatThreadClient:

var imageAttachment = await chatThreadClient.uploadImage(blob, file.name, {
  "onUploadProgress": reportProgressCallback
});

L’API prend en charge un blob d'image, une chaîne de nom de fichier et une fonction de rappel qui signale la progression du chargement.

Pour envoyer une image à un autre participant de conversation, vous devez :

  1. Chargez l’image via l’API uploadImage à partir de ChatThreadClient, puis enregistrez l’objet retourné.
  2. Composez le contenu du message et définissez une pièce jointe sur l’objet retourné que vous avez enregistré à l’étape précédente.
  3. Envoyez le nouveau message via l’API sendMessage à partir de ChatThreadClient.

Créez un sélecteur de fichiers qui accepte des images :

<label for="myfile">Attach images:</label>
<input id="upload" type="file" id="myfile" name="myfile" accept="image/*" multiple>
<input style="display: none;" id="upload-result"></input>

À présent, configurez un écouteur d’événements pour le moment où un changement d’état se produit :

document.getElementById("upload").addEventListener("change", uploadImages);

Vous devez créer une fonction pour le moment où l’état change :

var uploadedImageModels = [];

async function uploadImages(e) {
  const files = e.target.files;
  if (files.length === 0) {
    return;
  }
  for (let key in files) {
    if (files.hasOwnProperty(key)) {
        await uploadImage(files[key]);
    }
}
}

async function uploadImage(file) {
  const buffer = await file.arrayBuffer();
  const blob = new Blob([new Uint8Array(buffer)], {type: file.type });
  const url = window.URL.createObjectURL(blob);
  document.getElementById("upload-result").innerHTML += `<img src="${url}" height="auto" width="100" />`;
  let uploadedImageModel = await chatThreadClient.uploadImage(blob, file.name, {
    imageBytesLength: file.size
  });
  uploadedImageModels.push(uploadedImageModel);
}

Dans cet exemple, vous avez créé un FileReader pour lire chaque image en tant qu’images base64encodées, puis créer un Blob avant d’appeler l’API ChatSDK pour les charger. Vous avez créé un global uploadedImageModels pour enregistrer les modèles de données des images chargées à partir du Kit de développement logiciel (SDK) Chat.

Enfin, vous devez modifier l'écouteur d'événement sendMessageButton que vous avez créé précédemment pour attacher les images que vous avez chargées.

sendMessageButton.addEventListener("click", async () => {
  let message = messagebox.value;
  let attachments = uploadedImageModels;

    // Inject image tags for images we have selected
  // so they can be treated as inline images
  // Alternatively, we can use some third-party libraries 
  // to have a rich text editor with inline image support
  message += attachments.map((attachment) => `<img id="${attachment.id}" />`).join("");

  let sendMessageRequest = {
    content: message,
    attachments: attachments,
  };

  let sendMessageOptions = {
    senderDisplayName: "Jack",
    type: "html"
  };

  let sendChatMessageResult = await chatThreadClient.sendMessage(
    sendMessageRequest,
    sendMessageOptions
  );
  let messageId = sendChatMessageResult.id;
  uploadedImageModels = [];

  messagebox.value = "";
  document.getElementById("upload").value = "";
  console.log(`Message sent!, message id:${messageId}`);
});

C’est terminé. Exécutez maintenant le code pour le voir en action.

Exécuter le code

Les utilisateurs Webpack peuvent utiliser webpack-dev-server pour générer et exécuter votre application. Exécutez la commande suivante pour empaqueter l'hôte de votre application sur un serveur web local :

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

Démonstration

Ouvrez votre navigateur et accédez à http://localhost:8080/. Vous disposez d’une nouvelle section dans la zone d’envoi pour joindre des images.

Capture d’écran montrant un exemple d’application avec une section nouvellement ajoutée pour joindre des images.

Ensuite, vous pouvez sélectionner les images que vous souhaitez attacher.

Capture d’écran montrant un sélecteur de fichiers avec une liste d’images que les utilisateurs peuvent joindre à leurs messages.

Capture d’écran montrant l’exemple d’application avec deux images jointes.

L’utilisateur Teams doit maintenant recevoir l’image que vous venez d’envoyer quand il sélectionne Envoyer.

Capture d’écran montrant l’exemple d’application avec un message envoyé avec deux images incorporées.

Capture d’écran montrant le client Teams avec un message reçu avec deux images incorporées.

Ce tutoriel vous montre comment activer la prise en charge des images intégrées à l’aide du SDK de chat d'Azure Communication Services pour C#.

Dans ce tutoriel, vous allez apprendre à :

  • Gérez les images incorporées pour les nouveaux messages.

Conditions préalables

Objectif

  • Saisissez la propriété previewUri pour les pièces jointes d’images incorporées.

Gérer les images inline pour les nouveaux messages

Dans le guide de démarrage rapide, vous interrogez les messages et ajoutez de nouveaux messages à la messageList propriété. Vous développez cette fonctionnalité ultérieurement pour inclure l’analyse et l’extraction des images inline.

  CommunicationUserIdentifier currentUser = new(user_Id_);
  AsyncPageable<ChatMessage> allMessages = chatThreadClient.GetMessagesAsync();
  SortedDictionary<long, string> messageList = [];
  int textMessages = 0;
  await foreach (ChatMessage message in allMessages)
  {
      if (message.Type == ChatMessageType.Html || message.Type == ChatMessageType.Text)
      {
          textMessages++;
          var userPrefix = message.Sender.Equals(currentUser) ? "[you]:" : "";
          var strippedMessage = StripHtml(message.Content.Message);
          messageList.Add(long.Parse(message.SequenceId), $"{userPrefix}{strippedMessage}");
      }
  }

À partir de l’événement entrant de type ChatMessageReceivedEvent, la propriété nommée attachments contient des informations sur l’image inline. Il vous suffit d’afficher des images inline dans votre interface utilisateur.

public class ChatAttachment
{
    public ChatAttachment(string id, ChatAttachmentType attachmentType)
    public ChatAttachmentType AttachmentType { get }
    public string Id { get }
    public string Name { get }
    public System.Uri PreviewUrl { get }
    public System.Uri Url { get }
}

public struct ChatAttachmentType : System.IEquatable<AttachmentType>
{
    public ChatAttachmentType(string value)
    public static File { get }
    public static Image { get }
}

Le JSON suivant est un exemple de l'apparence de ChatAttachment pour une pièce jointe d'image :

"attachments": [
    {
        "id": "9d89acb2-c4e4-4cab-b94a-7c12a61afe30",
        "attachmentType": "image",
        "name": "Screenshot.png",
        "url": "https://contoso.communication.azure.com/chat/threads/19:9d89acb29d89acb2@thread.v2/images/9d89acb2-c4e4-4cab-b94a-7c12a61afe30/views/original?api-version=2023-11-03",
        "previewUrl": "https://contoso.communication.azure.com/chat/threads/19:9d89acb29d89acb2@thread.v2/images/9d89acb2-c4e4-4cab-b94a-7c12a61afe30/views/small?api-version=2023-11-03"
      }
]

Revenez en arrière et remplacez le code pour ajouter une logique supplémentaire pour analyser et extraire les pièces jointes de l’image :

  CommunicationUserIdentifier currentUser = new(user_Id_);
  AsyncPageable<ChatMessage> allMessages = chatThreadClient.GetMessagesAsync();
  SortedDictionary<long, string> messageList = [];
  int textMessages = 0;
  await foreach (ChatMessage message in allMessages)
  {
      // Get message attachments that are of type 'image'
      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);
      }

      // 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 = chatAttachmentImageUris.Count > 0 ? "[Attachments]:\n" + string.Join(",\n", chatAttachmentImageUris) : "";
          messageList.Add(long.Parse(message.SequenceId), $"{userPrefix}{strippedMessage}\n{chatAttachments}");
      }

Dans cet exemple, vous récupérez toutes les pièces jointes du message de type Image , puis récupérez chacune des images. Vous devez utiliser votre Token dans la partie Bearer de l'en-tête de la requête à des fins d'autorisation. Une fois l’image téléchargée, vous pouvez l’affecter à l’élément InlineImage de la vue.

Vous incluez également une liste des URI de pièce jointe à afficher avec le message dans la liste de messages texte.

Démonstration

  • Exécutez l’application à partir de l’environnement de développement intégré (IDE).
  • Entrez un lien de réunion Teams.
  • Rejoindre la conversation d’une réunion.
  • Acceptez l’utilisateur côté Teams.
  • Envoyez un message du côté Teams avec une image.

L’URL incluse dans le message apparaît dans la liste des messages. La dernière image reçue est affichée en bas de la fenêtre.

Étapes suivantes