Freigeben über


Tutorial: Aktivieren der Inline-Bildunterstützung in Ihrer Chat-App

Das Chat SDK ist für die nahtlose Zusammenarbeit mit Microsoft Teams konzipiert. Insbesondere bietet das Chat SDK eine Lösung zum Empfangen und Senden von Inlinebildern von und an Microsoft Teams-Benutzerinnen und -Benutzer.

In diesem Tutorial erfahren Sie, wie Sie die Inlinebildunterstützung mithilfe des Azure Communication Services Chat-SDK für JavaScript aktivieren.

Inlinebilder sind Bilder, die direkt in das Sendefeld des Teams-Clients kopiert und eingefügt werden. Informationen zu Bildern, die über das Menü Von diesem Gerät hochladen oder per Drag-and-Drop hochgeladen wurden, z. B. Bilder, die direkt in das Sendefeld in Teams gezogen wurden, finden Sie in diesem Tutorial als Teil des Dateifreigabefeatures. (Siehe Abschnitt „Verarbeiten von Bildanlagen“.)

Zum Kopieren eines Bildes haben Teams-Benutzer zwei Optionen:

  • Sie können das Kontextmenü des Betriebssystems verwenden, um die Bilddatei zu kopieren und sie dann in das Sendefeld des Teams-Clients einzufügen.
  • Verwenden von Tastenkombinationen

In diesem Tutorial erfahren Sie, was Sie tun müssen, wenn Sie:

Hinweis

Die Möglichkeit zum Senden von Inlinebildern befindet sich derzeit in der öffentlichen Vorschau. Sie ist nur für JavaScript verfügbar. Für den Empfang von Inlinebildern ist sie derzeit allgemein verfügbar. Sie ist sowohl für JavaScript als auch für C# in einem Teams-Interoperabilitätschat verfügbar.

Voraussetzungen

Beispielcode

Den finalen Code dieses Tutorials finden Sie auf GitHub.

Behandeln von empfangenen Inlinebildern in einem Ereignis „Neue Nachricht“

In diesem Abschnitt erfahren Sie, wie Inlinebilder gerendert werden können, die in den Nachrichteninhalt des Ereignisses „Neue Nachricht empfangen“ eingebettet sind.

In der Schnellstartanleitung haben Sie einen Ereignishandler für das Ereignis chatMessageReceived erstellt, der ausgelöst wird, wenn Sie eine neue Nachricht vom Teams-Benutzer erhalten. Sie haben auch den Inhalt der eingehenden Nachricht direkt an messageContainer angefügt, nachdem das Ereignis chatMessageReceived vom chatClient empfangen wurde, wie hier gezeigt:

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;
}

Aus dem eingehenden Ereignis vom Typ ChatMessageReceivedEvent enthält eine Eigenschaft mit dem Namen attachments Informationen zum Inlinebild. Das ist alles, was Sie benötigen, um Inlinebilder in Ihrer Benutzeroberfläche zu rendern:

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";

Kehren Sie nun zum vorherigen Code zurück, um zusätzliche Logik wie die folgenden Codeschnipsel hinzuzufügen:

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;
  });
}

In diesem Beispiel haben Sie zwei Hilfsfunktionen erstellt: fetchPreviewImages und setImgHandler. Die erste ruft das Vorschaubild direkt aus der previewURL ab, die in jedem ChatAttachment-Objekt mit einem Authentifizierungsheader bereitgestellt wird. Ebenso richten Sie ein onclick-Ereignis für jedes Bild in der setImgHandler-Funktion ein. Im Ereignishandler rufen Sie ein Bild mit voller Skalierung aus der Eigenschaft url aus dem ChatAttachment-Objekt mit einem Authentifizierungsheader ab.

Nun müssen Sie das Token auf globaler Ebene verfügbar machen, da Sie damit einen Authentifizierungsheader erstellen müssen. Sie müssen den folgenden Code ändern:

// 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;
   
   ...
}

Um das Bild in vollständiger Skalierung in einer Überlagerung anzuzeigen, müssen Sie auch eine neue Komponente hinzufügen:


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

Mit etwas 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
}

Nachdem Sie nun eine Überlagerung eingerichtet haben, arbeiten Sie jetzt an der Logik zum Rendern von Bildern in vollständiger Skalierung. Zur Erinnerung: Sie haben einen onClick-Ereignishandler erstellt, um eine fetchFullScaleImage-Funktion aufzurufen:


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';
}

Als Letztes möchten Sie die Möglichkeit hinzufügen, die Überlagerung beim Klicken auf das Bild zu schließen:

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

Jetzt haben Sie alle Änderungen vorgenommen, die zum Rendern von Inlinebildern für Nachrichten aus Echtzeitbenachrichtigungen erforderlich sind.

Ausführen des Codes

Webpack-Benutzer können webpack-dev-server verwenden, um Ihre App zu erstellen und auszuführen. Führen Sie den folgenden Befehl aus, um Ihren Anwendungshost auf einem lokalen Webserver zu bündeln:

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

Demo

Öffnen Sie Ihren Browser, und wechseln Sie zuhttp://localhost:8080/. Geben Sie die Besprechungs-URL und die Thread-ID ein. Senden Sie einige Inlinebilder vom Teams-Client.

Screenshot des Teams-Clients mit einer gesendeten Nachricht mit diesem Text: Here are some ideas, let me know what you think! (Hier sind einige Ideen, lassen Sie mich wissen, was Sie denken!) Die Nachricht enthält auch zwei Inlinebilder von nachgebildeten Zimmereinrichtungen.

Anschließend sollten Sie sehen, wie die neue Nachricht zusammen mit Vorschaubildern gerendert wird.

Screenshot einer Beispiel-App mit einer eingehenden Nachricht mit Inlinebildern.

Nachdem der Azure Communication Services-Benutzer das Vorschaubild ausgewählt hat, wird eine Überlagerung mit dem Bild in vollständiger Skalierung angezeigt, das vom Teams-Benutzer gesendet wurde.

Screenshot einer Beispiel-App mit einer Überlagerung eines Bildes in vollständiger Skalierung.

Behandeln des Sendens von Inlinebildern in einer Anforderung „Neue Nachricht“

Wichtig

Dieses Feature von Azure Communication Services befindet sich derzeit in der Vorschau.

Vorschau-APIs und -SDKs werden ohne Vereinbarung zum Servicelevel bereitgestellt. Es wird empfohlen, diese nicht für Produktionsworkloads zu verwenden. Einige Features werden möglicherweise nicht unterstützt oder bieten nur eingeschränkte Funktionalität.

Weitere Informationen finden Sie in den ergänzenden Nutzungsbestimmungen für Microsoft Azure-Vorschauversionen.

Zusätzlich zur Handhabung von Nachrichten mit Inlinebildern bietet das Chat-SDK für JavaScript auch eine Lösung, die es dem Kommunikationsbenutzer ermöglicht, Inlinebilder an die Microsoft Teams-Benutzer in einem Interoperabilitätschat zu senden.

Sehen Sie sich die neue API vom ChatThreadClient an:

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

Die API erfasst einen Bild-Blob, eine Zeichenkette mit dem Dateinamen und einen Funktionsrückruf, der den Fortschritt des Uploads meldet.

Um ein Bild an andere Chat-Teilnehmende zu senden, müssen Sie folgende Schritte ausführen:

  1. Laden Sie das Bild über die uploadImage-API vom ChatThreadClient hoch, und speichern Sie das zurückgegebene Objekt.
  2. Verfassen Sie den Nachrichteninhalt, und legen Sie die Anlage auf das zurückgegebene Objekt fest, das Sie im vorherigen Schritt gespeichert haben.
  3. Senden Sie die neue Nachricht über die sendMessage-API vom ChatThreadClient.

Erstellen Sie eine neue Dateiauswahl, die Bilder akzeptiert:

<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>

Richten Sie nun einen Ereignislistener für den Fall einer Statusänderung ein:

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

Sie müssen eine neue Funktion für eine Statusänderung erstellen:

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);
}

In diesem Beispiel haben Sie einen FileReader erstellt, um jedes Bild als base64-kodierte Bilder zu lesen. Dann wird ein Blob erstellt, bevor die ChatSDK-API aufgerufen wird, um sie hochzuladen. Sie haben eine globale uploadedImageModels erstellt, um die Datenmodelle hochgeladener Bilder aus dem Chat-SDK zu speichern.

Zuletzt müssen Sie den zuvor erstellten sendMessageButton-Ereignislistener ändern, um die hochgeladenen Bilder anzufügen.

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}`);
});

Das ist alles. Führen Sie nun den Code aus, um ihn in Aktion zu sehen.

Ausführen des Codes

Webpack-Benutzer können webpack-dev-server verwenden, um Ihre App zu erstellen und auszuführen. Führen Sie den folgenden Befehl aus, um Ihren Anwendungshost auf einem lokalen Webserver zu bündeln:

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

Demo

Öffnen Sie Ihren Browser, und wechseln Sie zuhttp://localhost:8080/. Im Sendefeld befindet sich ein neuer Bereich zum Anfügen von Bildern.

Screenshot einer Beispiel-App mit einem neu hinzugefügten Bereich zum Anfügen von Bildern.

Als Nächstes können Sie die Bilder auswählen, die Sie anfügen möchten.

Screenshot mit einer Dateiauswahl, die eine Liste von Bildern enthält, die Benutzer an ihre Nachrichten anfügen können.

Screenshot der Beispiel-App mit zwei angefügten Bildern.

Der Teams-Benutzer sollte nun das Bild erhalten, das Sie gerade gesendet haben, wenn er Senden auswählt.

Screenshot der Beispiel-App mit einer gesendeten Nachricht mit zwei eingebetteten Bildern.

Screenshot des Teams-Clients mit einer erhaltenen Nachricht mit zwei eingebetteten Bildern.

In diesem Tutorial erfahren Sie, wie Sie die Inlinebildunterstützung mit dem Azure Communication Services Chat-SDK für C# aktivieren.

In diesem Tutorial lernen Sie Folgendes:

  • Behandeln Sie Inlinebilder für neue Nachrichten.

Voraussetzungen

Ziel

  • Verwenden Sie die previewUri-Eigenschaft für Inlinebildanlagen.

Behandeln von Inlinebildern für neue Nachrichten

In der Schnellstartanleitung fragen Sie Nachrichten ab und fügen neue Nachrichten an die messageList-Eigenschaft an. Sie bauen später auf dieser Funktionalität auf, um das Parsen und Abrufen der Inlinebilder zu ermöglichen.

  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}");
      }
  }

Aus dem eingehenden Ereignis vom Typ ChatMessageReceivedEvent enthält die Eigenschaft mit dem Namen attachments Informationen zum Inlinebild. Das ist alles, was Sie benötigen, um Inlinebilder in Ihrer Benutzeroberfläche zu rendern.

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 }
}

Der folgende JSON-Code ist ein Beispiel dafür, wie ChatAttachment für eine Bildanlage aussehen könnte:

"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"
      }
]

Gehen Sie nun zurück, und ersetzen Sie den Code, um zusätzliche Logik zum Parsen und Abrufen der Bildanlagen hinzuzufügen:

  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}");
      }

In diesem Beispiel erfassen Sie alle Anlagen vom Typ Image der Nachricht. Dann rufen Sie die einzelnen Bilder ab. Sie müssen Ihr Token im Bearer-Teil des Anforderungsheaders zur Autorisierung verwenden. Nachdem das Bild heruntergeladen wurde, können Sie es dem InlineImage-Element der Ansicht zuweisen.

Außerdem schließen Sie eine Liste der Anlagen-URIs ein, die zusammen mit der Nachricht in der Textnachrichtenliste angezeigt werden sollen.

Demo

  • Führen Sie die Anwendung in der integrierten Entwicklungsumgebung (IDE) aus.
  • Geben Sie einen Teams-Besprechungslink ein.
  • Der-Besprechung beitreten.
  • Lassen Sie den Benutzer in Teams zu.
  • Senden Sie eine Nachricht in Teams mit einem Bild.

Die in der Nachricht enthaltene URL wird in der Nachrichtenliste angezeigt. Das zuletzt empfangene Bild wird am unteren Rand des Fensters gerendert.

Nächste Schritte