Delen via


Zelfstudie: Ondersteuning voor inline-installatiekopieën inschakelen in uw Chat-app

De Chat SDK is ontworpen om naadloos met Microsoft Teams te werken. Met name chat-SDK biedt een oplossing voor het ontvangen van inlineafbeeldingen en het verzenden van inlineafbeeldingen naar gebruikers van Microsoft Teams.

In deze zelfstudie leert u hoe u ondersteuning voor inline-installatiekopieën inschakelt met behulp van de Azure Communication Services Chat SDK voor JavaScript.

Inline-afbeeldingen zijn afbeeldingen die rechtstreeks in het verzendvak van de Teams-client worden gekopieerd en geplakt. Voor afbeeldingen die zijn geüpload via het menu Uploaden vanaf dit apparaat of via slepen en neerzetten, zoals afbeeldingen die rechtstreeks naar het verzendvak in Teams zijn gesleept, moet u naar deze zelfstudie verwijzen als onderdeel van de functie voor het delen van bestanden. (Zie de sectie 'Afbeeldingsbijlagen verwerken'.)

Als u een afbeelding wilt kopiëren, hebben Teams-gebruikers twee opties:

  • Gebruik het contextmenu van het besturingssysteem om het afbeeldingsbestand te kopiëren en plak het in het verzendvak van de Teams-client.
  • Gebruik sneltoetsen.

In deze zelfstudie leert u wat u moet doen wanneer u:

Notitie

De mogelijkheid om inlineafbeeldingen te verzenden, is momenteel beschikbaar in openbare preview. Deze is alleen beschikbaar voor JavaScript. Voor het ontvangen van inlineafbeeldingen is deze momenteel algemeen beschikbaar. Het is beschikbaar voor zowel JavaScript als C# in een teams-interoperabiliteitschat.

Vereisten

Voorbeeldcode

Zoek de voltooide code van deze zelfstudie op GitHub.

Ontvangen inlineafbeeldingen verwerken in een nieuwe berichtgebeurtenis

In deze sectie leert u hoe u inlineafbeeldingen kunt weergeven die zijn ingesloten in de berichtinhoud van een nieuwe ontvangen gebeurtenis.

In de quickstart hebt u een gebeurtenis-handler gemaakt voor de chatMessageReceived gebeurtenis, die wordt geactiveerd wanneer u een nieuw bericht van de Teams-gebruiker ontvangt. U voegt ook binnenkomende berichtinhoud messageContainer rechtstreeks toe bij het ontvangen van de gebeurtenis van de chatMessageReceived chatClient, zoals deze:

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

Vanuit de binnenkomende gebeurtenis van het type ChatMessageReceivedEventbevat een eigenschap met de naam attachments informatie over de inlineafbeelding. U hoeft alleen inlineafbeeldingen weer te geven in uw gebruikersinterface:

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

Ga nu terug naar de vorige code om extra logica toe te voegen, zoals de volgende codefragmenten:

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 dit voorbeeld hebt u twee helperfuncties gemaakt en fetchPreviewImages setImgHandler. De eerste haalt de voorbeeldafbeelding rechtstreeks op van de previewURL opgegeven in elk ChatAttachment object met een verificatieheader. Op dezelfde manier stelt u een onclick gebeurtenis in voor elke afbeelding in de functie setImgHandler. In de gebeurtenis-handler haalt u een afbeelding op volledige schaal op uit de eigenschap url van het ChatAttachment object met een verificatieheader.

Nu moet u het token beschikbaar maken op het globale niveau, omdat u er een verificatieheader mee moet maken. U moet de volgende code wijzigen:

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

Als u de afbeelding op volledige schaal in een overlay wilt weergeven, moet u ook een nieuw onderdeel toevoegen:


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

Met een aantal 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 u een overlay hebt ingesteld, is het tijd om aan de logica te werken om afbeeldingen op volledige schaal weer te geven. U hebt een onClick gebeurtenis-handler gemaakt om een functie fetchFullScaleImageaan te roepen:


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

Een laatste wat u wilt toevoegen, is de mogelijkheid om de overlay te sluiten wanneer op de afbeelding wordt geklikt:

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

Nu hebt u alle wijzigingen aangebracht die u nodig hebt om inlineafbeeldingen weer te geven voor berichten die afkomstig zijn van realtime meldingen.

De code uitvoeren

Webpack-gebruikers kunnen uw webpack-dev-server app bouwen en uitvoeren. Voer de volgende opdracht uit om uw toepassingshost te bundelen op een lokale webserver:

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

Demo

Open uw browser en ga naar http://localhost:8080/. Voer de URL van de vergadering en de thread-id in. Verzend enkele inlineafbeeldingen van de Teams-client.

Schermopname van een Teams-client met een verzonden bericht dat wordt gelezen: Hier volgen enkele ideeën, laat me weten wat u ervan vindt. Het bericht bevat ook twee inline afbeeldingen van ruimte-interieursimuleerden.

Vervolgens ziet u dat het nieuwe bericht samen met voorbeeldafbeeldingen wordt weergegeven.

Schermopname van een voorbeeld-app met een binnenkomend bericht met inlineafbeeldingen.

Nadat de Azure Communication Services-gebruiker de voorbeeldafbeelding heeft geselecteerd, wordt er een overlay weergegeven met de volledige afbeelding die door de Teams-gebruiker wordt verzonden.

Schermopname van een voorbeeld-app met een overlay van een afbeelding op volledige schaal.

Het verzenden van inlineafbeeldingen in een nieuwe berichtaanvraag afhandelen

Belangrijk

Deze functie van Azure Communication Services is momenteel beschikbaar als preview-versie.

Preview-API's en SDK's worden aangeboden zonder een service level agreement. U wordt aangeraden deze niet te gebruiken voor productieworkloads. Sommige functies worden mogelijk niet ondersteund of hebben mogelijk beperkte mogelijkheden.

Raadpleeg aanvullende gebruiksvoorwaarden voor Microsoft Azure Previews voor meer informatie.

Naast het verwerken van berichten met inlineafbeeldingen biedt de Chat SDK voor JavaScript ook een oplossing waarmee de communicatiegebruiker inlineafbeeldingen naar de Microsoft Teams-gebruiker kan verzenden in een interoperabiliteitschat.

Bekijk de nieuwe API vanuit ChatThreadClient:

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

De API maakt gebruik van een afbeeldingsblob, bestandsnaamtekenreeks en een functie-callback die de voortgang van het uploaden rapporteert.

Als u een afbeelding naar een andere chatdeelnemer wilt verzenden, moet u het volgende doen:

  1. Upload de afbeelding via de uploadImage API van ChatThreadClienten sla het geretourneerde object op.
  2. Stel de inhoud van het bericht samen en stel een bijlage in op het geretourneerde object dat u in de vorige stap hebt opgeslagen.
  3. Verzend het nieuwe bericht via de sendMessage API van ChatThreadClient.

Maak een nieuwe bestandskiezer die afbeeldingen accepteert:

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

Stel nu een gebeurtenislistener in voor wanneer er een statuswijziging is:

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

U moet een nieuwe functie maken voor wanneer de status verandert:

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 dit voorbeeld hebt u een FileReader gemaakt om elke afbeelding te lezen als base64-gecodeerde afbeeldingen en vervolgens een Blob afbeelding te maken voordat u de ChatSDK-API aanroept om deze te uploaden. U hebt een globaal uploadedImageModels bestand gemaakt om de gegevensmodellen van geüploade afbeeldingen op te slaan vanuit de Chat SDK.

Ten slotte moet u de sendMessageButton gebeurtenislistener wijzigen die u eerder hebt gemaakt om de afbeeldingen die u hebt geüpload, toe te voegen.

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

Dat is alles. Voer nu de code uit om deze in actie te zien.

De code uitvoeren

Webpack-gebruikers kunnen uw webpack-dev-server app bouwen en uitvoeren. Voer de volgende opdracht uit om uw toepassingshost te bundelen op een lokale webserver:

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

Demo

Open uw browser en ga naar http://localhost:8080/. U hebt een nieuwe sectie in het vak Verzenden om afbeeldingen toe te voegen.

Schermopname van een voorbeeld-app met een nieuw toegevoegde sectie om afbeeldingen toe te voegen.

Vervolgens kunt u de afbeeldingen selecteren die u wilt bijvoegen.

Schermopname van een bestandskiezer met een lijst met afbeeldingen die gebruikers aan hun berichten kunnen toevoegen.

Schermopname van de voorbeeld-app met twee afbeeldingen bijgevoegd.

De Teams-gebruiker moet nu de afbeelding ontvangen die u zojuist hebt verzonden wanneer ze Verzenden selecteren.

Schermopname van de voorbeeld-app met een verzonden bericht met twee ingesloten afbeeldingen.

Schermopname van de Teams-client met een ontvangen bericht met twee ingesloten afbeeldingen.

In deze zelfstudie leert u hoe u ondersteuning voor inline-installatiekopieën inschakelt met behulp van de Azure Communication Services Chat SDK voor C#.

In deze zelfstudie leert u het volgende:

  • Inlineafbeeldingen verwerken voor nieuwe berichten.

Vereisten

Goal

  • Pak de previewUri eigenschap voor inlineafbeeldingsbijlagen.

Inlineafbeeldingen voor nieuwe berichten verwerken

In de quickstart peilt u berichten en voegt u nieuwe berichten toe aan de messageList eigenschap. U bouwt later voort op deze functionaliteit om het parseren en ophalen van de inlineafbeeldingen op te nemen.

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

Vanuit de binnenkomende gebeurtenis van het type ChatMessageReceivedEventbevat de eigenschap met de naam attachments informatie over de inlineafbeelding. Het is alles wat u nodig hebt om inlineafbeeldingen weer te geven in uw gebruikersinterface.

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

De volgende JSON is een voorbeeld van hoe ChatAttachment het eruit kan zien voor een afbeeldingsbijlage:

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

Ga nu terug en vervang de code om extra logica toe te voegen om de afbeeldingsbijlagen te parseren en op te halen:

  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 dit voorbeeld pakt u alle bijlagen uit het bericht van het type Image en haalt u vervolgens elk van de afbeeldingen op. U moet uw Token in het Bearer gedeelte van de aanvraagheader gebruiken voor autorisatiedoeleinden. Nadat de afbeelding is gedownload, kunt u deze toewijzen aan het InlineImage element van de weergave.

U voegt ook een lijst met de bijlage-URI's toe die samen met het bericht in de berichtenlijst moeten worden weergegeven.

Demo

  • Voer de toepassing uit vanuit de integrated development environment (IDE).
  • Voer een koppeling naar een Teams-vergadering in.
  • Neem deel aan de vergadering.
  • Geef de gebruiker toe aan de kant van Teams.
  • Verzend een bericht aan de kant van Teams met een afbeelding.

De URL die is opgenomen in het bericht, wordt weergegeven in de berichtenlijst. De laatst ontvangen afbeelding wordt onderaan het venster weergegeven.

Volgende stappen