Partager via


Tutoriel : Activer la prise en charge des images en ligne dans votre application de conversation instantanée

Le SDK Conversation est conçu pour fonctionner de manière fluide 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 didacticiel, vous apprendrez à activer la prise en charge des images en ligne à l’aide du SDK Azure Communication Services Chat pour JavaScript.

Les images en ligne sont des images copiées et collées directement dans la zone d’envoi du client Teams. Pour les images téléchargées via le menu Télécharger à partir de cet appareil ou par glisser-déposer, telles que les images glissées directement vers la boîte d'envoi dans Teams, vous devez vous référer à 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 en ligne est actuellement disponible en préversion publique. Il est disponible uniquement pour JavaScript. Pour recevoir des images en ligne, il est actuellement généralement disponible. Il est disponible pour JavaScript et C# dans une conversation d’interopérabilité Teams.

Prérequis

Exemple de code

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

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

Dans cette section, vous apprendrez comment restituer des images en ligne intégrées dans le contenu du message d'un nouvel événement de message reçu.

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

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 en ligne. C'est tout ce dont vous avez besoin pour afficher des images en ligne 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 extrait l’image d’aperçu directement à partir du previewURL fourni dans chaque objet ChatAttachment avec un en-tête d’authentification. De même, vous configurez un événement onclick pour chaque image de 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 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 des 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 nécessaires pour afficher les images en ligne pour les messages provenant de notifications en temps réel.

Exécuter le code

Les utilisateurs de Webpack peuvent l'utiliser webpack-dev-server pour créer et exécuter votre application. Exécutez la commande suivante pour regrouper 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

Démo

Ouvrez votre navigateur et accédez àhttp://localhost:8080/. Saisissez l’URL de la réunion et l’ID du thread. Envoyez des images en ligne depuis le 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 en ligne de maquettes intérieures de pièces.

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 en ligne dans une nouvelle demande de message

Important

Cette fonctionnalité d’Azure Communication Services est actuellement en préversion.

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 être limitées ou non prises en charge.

Pour plus d’informations, consultez Conditions d’utilisation supplémentaires relatives aux préversions de Microsoft Azure.

En plus de gérer les messages avec des images en ligne, le SDK Chat pour JavaScript fournit également une solution permettant à l'utilisateur de communication d'envoyer des images en ligne à 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 un objet blob d’images, une chaîne de nom de fichier et un rappel de fonction 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 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 sous base64- forme d'images codées, puis créez un Blob avant d'appeler l'API ChatSDK pour les télécharger. Vous avez créé un fichier global uploadedImageModels pour enregistrer les modèles de données des images téléchargées à partir du SDK Chat.

Enfin, vous devez modifier l’écouteur d’événements 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}`);
});

Vous avez terminé. Exécutez maintenant le code pour le voir en action.

Exécuter le code

Les utilisateurs de Webpack peuvent l'utiliser webpack-dev-server pour créer et exécuter votre application. Exécutez la commande suivante pour regrouper 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

Démo

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 lorsqu’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 didacticiel vous montre comment activer la prise en charge des images en ligne à l’aide du SDK Chat Azure Communication Services pour C#.

Dans ce tutoriel, vous allez apprendre à :

  • Gérez les images en ligne pour les nouveaux messages.

Prérequis

But

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

Gérer les images en ligne des nouveaux messages

Dans le démarrage rapide, vous recherchez des messages et ajoutez de nouveaux messages à la propriété messageList. Vous développerez cette fonctionnalité ultérieurement pour inclure l'analyse et la récupération des images en ligne.

  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 en ligne. C'est tout ce dont vous avez besoin pour restituer des images en ligne 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 code JSON suivant est un exemple de ce que ChatAttachment peut ressembler à 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 demande à 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émo

  • Exécutez l’application à partir de l’environnement de développement intégré (IDE).
  • Entrez un lien de réunion Teams.
  • Rejoindre la réunion.
  • Admettez 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