Dela via


Självstudie: Aktivera stöd för infogade avbildningar i chattappen

Chat SDK är utformat för att fungera sömlöst med Microsoft Teams. Mer specifikt tillhandahåller Chat SDK en lösning för att ta emot infogade bilder och skicka infogade bilder till användare från Microsoft Teams.

I den här självstudien får du lära dig hur du aktiverar stöd för infogade avbildningar med hjälp av Azure Communication Services Chat SDK för JavaScript.

Infogade bilder är bilder som kopieras och klistras in direkt i teams-klientens sändningsruta. För bilder som laddas upp via menyn Ladda upp från den här enheten eller dra och släpp, till exempel bilder som dras direkt till sändningsrutan i Teams, måste du referera till den här självstudien som en del av fildelningsfunktionen. (Se avsnittet "Hantera bifogade bilder")

Teams-användare har två alternativ för att kopiera en avbildning:

  • Använd operativsystemets snabbmeny för att kopiera avbildningsfilen och klistra in den i rutan Skicka för teams-klienten.
  • Använd kortkommandon.

I den här självstudien lär du dig vad du behöver göra när du:

Kommentar

Möjligheten att skicka infogade bilder är för närvarande tillgänglig i offentlig förhandsversion. Det är bara tillgängligt för JavaScript. För att ta emot infogade bilder är det för närvarande allmänt tillgängligt. Den är tillgänglig för både JavaScript och C# i en Teams-samverkanschatt.

Förutsättningar

Exempelkod

Hitta den färdiga koden för den här självstudien på GitHub.

Hantera mottagna infogade bilder i en ny meddelandehändelse

I det här avsnittet får du lära dig hur du kan återge infogade bilder inbäddade i meddelandeinnehållet i en ny händelse som tas emot.

I snabbstarten skapade du en händelsehanterare för chatMessageReceived händelsen, som utlöses när du får ett nytt meddelande från Teams-användaren. Du lägger också till messageContainer inkommande meddelandeinnehåll direkt när du chatMessageReceived tar emot händelsen från chatClient, så här:

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

Från den inkommande händelsen av typen ChatMessageReceivedEventinnehåller en egenskap med namnet attachments information om den infogade avbildningen. Det är allt du behöver för att rendera infogade bilder i användargränssnittet:

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

Gå nu tillbaka till föregående kod för att lägga till lite extra logik, till exempel följande kodfragment:

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

I det här exemplet har du skapat två hjälpfunktioner och fetchPreviewImagessetImgHandler. Den första hämtar förhandsgranskningsbilden direkt från det previewURL som anges i varje ChatAttachment objekt med en autentiseringsrubrik. På samma sätt konfigurerar du en onclick händelse för varje bild i funktionen setImgHandler. I händelsehanteraren hämtar du en fullskalig bild från egenskapen url från ChatAttachment objektet med ett autentiseringshuvud.

Nu måste du exponera token på global nivå eftersom du behöver skapa en autentiseringsrubrik med den. Du måste ändra följande kod:

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

Om du vill visa bilden i full skala i ett överlägg måste du också lägga till en ny komponent:


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

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

Nu när du har konfigurerat ett överlägg är det dags att arbeta med logiken för att återge fullskalig avbildningar. Kom ihåg att du skapade en onClick händelsehanterare för att anropa en funktion 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';
}

En sista sak du vill lägga till är möjligheten att stänga överlägget när bilden klickas:

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

Nu har du gjort alla ändringar som du behöver för att återge infogade bilder för meddelanden som kommer från realtidsmeddelanden.

Kör koden

Webpack-användare kan använda webpack-dev-server för att skapa och köra din app. Kör följande kommando för att paketera programvärden på en lokal webbserver:

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

Demo

Öppna din webbläsare och gå till http://localhost:8080/. Ange mötes-URL:en och tråd-ID:t. Skicka några infogade avbildningar från Teams-klienten.

Skärmbild som visar en Teams-klient med ett skickat meddelande som lyder: Här är några idéer, låt mig veta vad du tycker! Meddelandet innehåller också två infogade bilder av rumsinteriörer.

Sedan bör du se det nya meddelandet återges tillsammans med förhandsgranskningsbilder.

Skärmbild som visar en exempelapp med ett inkommande meddelande med infogade bilder.

När Azure Communication Services-användaren har valt förhandsgranskningsbilden visas ett överlägg med den fullskalig bild som skickas av Teams-användaren.

Skärmbild som visar en exempelapp med ett överlägg av en fullskalig bild.

Hantera sändning av infogade bilder i en ny meddelandebegäran

Viktigt!

Den här funktionen i Azure Communication Services är för närvarande i förhandsversion.

Förhandsversions-API:er och SDK:er tillhandahålls utan ett serviceavtal. Vi rekommenderar att du inte använder dem för produktionsarbetsbelastningar. Vissa funktioner kanske inte stöds, eller så kan de ha begränsade funktioner.

Mer information finns i Kompletterande användningsvillkor för Förhandsversioner av Microsoft Azure.

Förutom att hantera meddelanden med infogade bilder tillhandahåller Chat SDK för JavaScript också en lösning som gör det möjligt för kommunikationsanvändaren att skicka infogade bilder till Microsoft Teams-användaren i en samverkanschatt.

Ta en titt på det nya API:et från ChatThreadClient:

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

API:et tar in en bildblob, filnamnssträng och ett funktionsåteranrop som rapporterar överföringsförloppet.

Om du vill skicka en bild till en annan chattdeltagare måste du:

  1. Ladda upp avbildningen via API:et uploadImage från ChatThreadClientoch spara det returnerade objektet.
  2. Skriv meddelandeinnehållet och ange en bifogad fil till det returnerade objekt som du sparade i föregående steg.
  3. Skicka det nya meddelandet via API:et sendMessage från ChatThreadClient.

Skapa en ny filväljare som accepterar avbildningar:

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

Konfigurera nu en händelselyssnare för när det sker en tillståndsändring:

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

Du måste skapa en ny funktion för när tillståndet ändras:

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

I det här exemplet har du skapat en FileReader för att läsa varje bild som base64-kodade bilder och sedan skapa en Blob innan du anropar ChatSDK-API:et för att ladda upp dem. Du skapade en global uploadedImageModels för att spara datamodellerna för uppladdade bilder från Chat SDK.

Slutligen måste du ändra den händelselyssnare sendMessageButton som du skapade tidigare för att bifoga de bilder som du laddade upp.

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

Klart! Kör nu koden för att se den i praktiken.

Kör koden

Webpack-användare kan använda webpack-dev-server för att skapa och köra din app. Kör följande kommando för att paketera programvärden på en lokal webbserver:

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

Demo

Öppna din webbläsare och gå till http://localhost:8080/. Du har ett nytt avsnitt i rutan Skicka för att bifoga bilder.

Skärmbild som visar en exempelapp med ett nyligen lagt avsnitt för att bifoga bilder.

Därefter kan du välja de bilder som du vill koppla.

Skärmbild som visar en filväljare med en lista över bilder som användare kan koppla till sina meddelanden.

Skärmbild som visar exempelappen med två bifogade bilder.

Teams-användaren bör nu ta emot den avbildning som du just skickade ut när de väljer Skicka.

Skärmbild som visar exempelappen med ett skickat meddelande med två inbäddade bilder.

Skärmbild som visar Teams-klienten med ett mottaget meddelande med två inbäddade bilder.

Den här självstudien visar hur du aktiverar stöd för infogade avbildningar med hjälp av Azure Communication Services Chat SDK för C#.

I den här självstudien lär du dig att:

  • Hantera infogade bilder för nya meddelanden.

Förutsättningar

Goal

  • Hämta egenskapen previewUri för infogade bildbilagor.

Hantera infogade bilder för nya meddelanden

I snabbstarten söker du efter meddelanden och lägger till nya meddelanden i egenskapenmessageList. Du bygger vidare på den här funktionen senare för att inkludera parsning och hämtning av infogade bilder.

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

Från den inkommande händelsen av typen ChatMessageReceivedEventinnehåller egenskapen med namnet attachments information om den infogade avbildningen. Det är allt du behöver för att återge infogade bilder i användargränssnittet.

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

Följande JSON är ett exempel på hur ChatAttachment det kan se ut för en bifogad bild:

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

Gå nu tillbaka och ersätt koden för att lägga till extra logik för att parsa och hämta bildbilagor:

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

I det här exemplet hämtar du alla bifogade filer från meddelandet av typen Image och hämtar sedan var och en av bilderna. Du måste använda din Token i Bearer delen av begärandehuvudet för auktoriseringsändamål. När avbildningen har laddats ned kan du tilldela den till elementet InlineImage i vyn.

Du inkluderar också en lista över de bifogade URI:er som ska visas tillsammans med meddelandet i textmeddelandelistan.

Demo

  • Kör programmet från den integrerade utvecklingsmiljön (IDE).
  • Ange en Teams-möteslänk.
  • Ansluta till mötet.
  • Ta emot användaren på Teams-sidan.
  • Skicka ett meddelande från Teams-sidan med en bild.

URL:en som ingår i meddelandet visas i meddelandelistan. Den senast mottagna bilden återges längst ned i fönstret.

Nästa steg