Freigeben über


Lernprogramm: Aktivieren der Inlinebildunterstützung in Ihrer Chat-App

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

In diesem Lernprogramm 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. Für Bilder, die über das Menü "Hochladen von diesem Gerät" oder per Drag-and-Drop hochgeladen wurden, wie beispielsweise Bilder, die direkt in das Sendeformular in Teams gezogen wurden, sollten Sie dieses Tutorial als Anleitung im Rahmen der Dateifreigabefunktion heranziehen. (Siehe den Abschnitt "Behandeln von Bildanhängen".)

Um ein Bild zu kopieren, haben Teams-Benutzer zwei Optionen:

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

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

Hinweis

Die Möglichkeit zum Senden von Inlinebildern ist derzeit in der öffentlichen Vorschau verfügbar. Es ist nur für JavaScript verfügbar. Für den Empfang von Inlinebildern ist sie derzeit allgemein verfügbar. Es 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 Sie Inlinebilder rendern können, die in den Nachrichteninhalt eines neuen empfangenen Nachrichtenereignisses eingebettet sind.

In der Schnellstartanleitung haben Sie einen Ereignishandler für das chatMessageReceived Ereignis erstellt, der ausgelöst wird, wenn Sie eine neue Nachricht vom Teams-Benutzer erhalten. Sie hängen auch den Inhalt der eingehenden Nachrichten direkt an messageContainer an, sobald Sie das chatMessageReceived-Ereignis von der chatClient erhalten, wie folgt:

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. Es 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 hinzuzufügen, z. B. die folgenden Codeausschnitte:

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 fetchPreviewImages und setImgHandler erstellt. 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 Funktion setImgHandlerein. Im Ereignishandler rufen Sie ein Bild mit voller Skalierung aus der Eigenschaft url aus dem ChatAttachment-Objekt mit einem Authentifizierungsheader ab.

Jetzt müssen Sie das Token auf globaler Ebene verfügbar machen, da Sie einen Authentifizierungsheader damit 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 im Vollmaßstab 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, ist es an der Zeit, an der Logik zu arbeiten, um Bilder im Vollformat zu rendern. Erinnern Sie sich daran, dass Sie einen onClick Ereignishandler zum Aufrufen einer Funktion fetchFullScaleImageerstellt haben:


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 Sie zum Rendern von Inlinebildern für Nachrichten benötigen, die aus Echtzeitbenachrichtigungen stammen.

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 einen Browser, und gehen Sie zu http://localhost:8080/. Geben Sie die Besprechungs-URL und die Thread-ID ein. Senden Sie einige Inlinebilder vom Teams-Client.

Screenshot eines Teams-Clients mit einer gesendeten Nachricht, in der steht: Hier sind einige Ideen, lassen Sie mich wissen, was Sie denken! Die Nachricht enthält auch zwei eingebettete Bilder von Innenraum-Mockups.

Anschließend sollte die neue Nachricht zusammen mit Vorschaubildern angezeigt werden.

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 vollständigen Bild angezeigt, das vom Teams-Benutzer gesendet wird.

Screenshot einer Beispiel-App mit einer Überlagerung eines Bilds im Vollmaßstab.

Bearbeiten des Sendens von Inlinebildern in einer neuen Anfragenachricht

Wichtig

Dieses Feature von Azure Communication Services befindet sich derzeit in der Vorschau. Features in der Vorschau sind öffentlich verfügbar und können von allen neuen und vorhandenen Microsoft-Kunden verwendet werden.

Vorschau-APIs und -SDKs werden ohne Vereinbarung zum Servicelevel bereitgestellt. Es wird empfohlen, diese nicht für Produktionsworkloads zu verwenden. Bestimmte Features werden möglicherweise nicht unterstützt oder Funktionen sind eingeschränkt.

Weitere Informationen finden Sie unter Zusätzliche Nutzungsbestimmungen für Microsoft Azure-Vorschauen.

Neben der Behandlung von Nachrichten mit Inlinebildern bietet das Chat SDK für JavaScript auch eine Lösung, mit der der Kommunikationsbenutzer Inlinebilder in einem Interoperabilitätschat an den Microsoft Teams-Benutzer senden kann.

Sehen Sie sich die neue API von ChatThreadClient:

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

Die API übernimmt ein Bild-BLOB, eine Dateinamenzeichenfolge und einen Funktionsrückruf, der den Uploadfortschritt meldet.

Um ein Bild an einen anderen Chatteilnehmer zu senden, müssen Sie:

  1. Laden Sie das Bild über die uploadImage API von ChatThreadClient hoch, und speichern Sie das zurückgegebene Objekt.
  2. Verfassen Sie den Nachrichteninhalt, und legen Sie eine Anlage auf das zurückgegebene Objekt fest, das Sie im vorherigen Schritt gespeichert haben.
  3. Senden Sie die neue Nachricht über die sendMessage API von 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 Ereignis-Listener ein, wenn eine Statusänderung auftritt:

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

Sie müssen eine neue Funktion erstellen, wenn sich der Zustand ändert:

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.

Schließlich müssen Sie den sendMessageButton zuvor erstellten 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 war's. 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 einen Browser, und gehen Sie zu http://localhost:8080/. Sie haben einen neuen Abschnitt im Sendefeld, um Bilder anzufügen.

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

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

Screenshot einer Dateiauswahl mit einer Liste von Bildern, 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 empfangen, 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 empfangenen Nachricht mit zwei eingebetteten Bildern.

In diesem Lernprogramm erfahren Sie, wie Sie die Unterstützung von Inlinebildern mithilfe des Azure Communication Services Chat SDK für C# aktivieren.

In diesem Tutorial lernen Sie Folgendes:

  • Inline-Bilder für neue Nachrichten verwalten.

Voraussetzungen

Ziel

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

Verwalten von Inline-Bildern für neue Nachrichten

In der Schnellstartanleitung können Sie Nachrichten abfragen und neue Nachrichten an die messageList Eigenschaft anfügen. Sie bauen später auf dieser Funktionalität auf, um die Analyse und das Abrufen der Inlinebilder einzuschließen.

  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. Es 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 als Bildanhang 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 zurück und ersetzen Sie den Code, um zusätzliche Logik zum Analysieren und Abrufen der Bildanhänge 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 greifen Sie alle Anlagen aus der Nachricht des Typs Image ab und rufen dann jedes der 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 fügen Sie eine Liste der Anlagen-URIs hinzu, die zusammen mit der Nachricht in der Textnachrichtenliste angezeigt werden sollen.

Demo

  • Führen Sie die Anwendung aus der integrierten Entwicklungsumgebung (IDE) aus.
  • Geben Sie einen Teams-Besprechungslink ein.
  • Nehmen Sie an der Besprechung teil.
  • Lassen Sie den Benutzer in Teams zu.
  • Senden Sie eine Nachricht von der Teams-Seite 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