Sdílet prostřednictvím


Kurz: Povolení podpory vložených obrázků v chatovací aplikaci

Sada SDK chatu je navržená tak, aby bez problémů fungovala s Microsoft Teams. Konkrétně sada SDK chatu poskytuje řešení pro příjem vložených obrázků a odesílání vložených obrázků uživatelům z Microsoft Teams.

V tomto kurzu se dozvíte, jak povolit podporu vložených imagí pomocí sady SDK chatu služby Azure Communication Services pro JavaScript.

Vložené obrázky jsou obrázky, které se zkopírují a vloží přímo do pole pro odeslání klienta Teams. U obrázků nahraných prostřednictvím nabídky Nahrát z této nabídky zařízení nebo přetažení, jako jsou například obrázky přetažené přímo do pole pro odeslání v Aplikaci Teams, je potřeba použít tento kurz jako součást funkce sdílení souborů. (Viz část Popisovat přílohy obrázků.)

Pokud chcete obrázek zkopírovat, mají uživatelé Teams dvě možnosti:

  • Pomocí místní nabídky operačního systému zkopírujte soubor image a vložte ho do pole pro odeslání klienta Teams.
  • Používejte klávesové zkratky.

V tomto kurzu se dozvíte, co potřebujete udělat, když:

Poznámka:

Možnost odesílat vložené obrázky je aktuálně dostupná ve verzi Public Preview. Je k dispozici pouze pro JavaScript. Pro příjem vložených obrázků je aktuálně obecně dostupná. Je k dispozici pro JavaScript i C# v chatu s interoperabilitou Teams.

Požadavky

Ukázkový kód

Najděte finalizovaný kód tohoto kurzu na GitHubu.

Zpracování přijatých vložených obrázků v nové události zprávy

V této části se dozvíte, jak můžete vykreslit vložené obrázky vložené do obsahu zprávy nové přijaté události.

V rychlém startu jste vytvořili obslužnou rutinu události pro chatMessageReceived událost, která se aktivuje při přijetí nové zprávy od uživatele Teams. Obsah příchozí zprávy také připojíte přímo při messageContainer přijetí chatMessageReceived události z objektu chatClient, například takto:

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

Z příchozí události typu ChatMessageReceivedEvent, vlastnost pojmenovaná attachments obsahuje informace o vloženém obrázku. Stačí vykreslit vložené obrázky v uživatelském rozhraní:

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

Teď se vraťte k předchozímu kódu a přidejte další logiku, například následující fragmenty kódu:

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

V tomto příkladu jste vytvořili dvě pomocné funkce fetchPreviewImages a setImgHandler. První načte obrázek náhledu přímo ze zadaného objektu previewURLChatAttachment s hlavičkou ověřování. Podobně nastavíte onclick událost pro každý obrázek ve funkci setImgHandler. V obslužné rutině události načtete obrázek v plném měřítku z vlastnosti url z objektu ChatAttachment s hlavičkou ověřování.

Teď musíte token zveřejnit na globální úrovni, protože s ním potřebujete vytvořit hlavičku ověřování. Musíte upravit následující kód:

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

Pokud chcete zobrazit celý obrázek v překryvu, musíte také přidat novou komponentu:


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

S některými šablonami stylů 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
}

Teď, když máte nastavenou překryvnou vrstvu, je čas pracovat na logice pro vykreslení plně škálovatelných obrázků. Vzpomeňte si, že jste vytvořili obslužnou rutinu onClick události pro volání funkce 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';
}

Jednou z posledních věcí, kterou chcete přidat, je možnost zavřít překryv při kliknutí na obrázek:

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

Teď jste provedli všechny změny, které potřebujete k vykreslení vložených obrázků pro zprávy, které pocházejí z oznámení v reálném čase.

Spuštění kódu

Uživatelé webpacku můžou použít webpack-dev-server k sestavení a spuštění aplikace. Spuštěním následujícího příkazu sbalte hostitele aplikace na místním webovém serveru:

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

Ukázka

Otevřete prohlížeč a přejděte na http://localhost:8080/. Zadejte adresu URL schůzky a ID vlákna. Odešlete některé vložené obrázky z klienta Teams.

Snímek obrazovky znázorňující klienta Teams s odeslanou zprávou, která čte: Tady jsou některé nápady, dejte mi vědět, co si myslíte! Zpráva obsahuje také dva vložené obrázky vnitřních mockupů místnosti.

Pak by se měla zobrazit nová zpráva vykreslená společně s obrázky náhledu.

Snímek obrazovky znázorňující ukázkovou aplikaci s příchozí zprávou s vloženými obrázky

Jakmile uživatel Azure Communication Services vybere obrázek náhledu, zobrazí se překryvný obrázek s plně škálovatelným obrázkem odeslaným uživatelem Teams.

Snímek obrazovky znázorňující ukázkovou aplikaci s překrytím úplného obrázku

Zpracování odesílání vložených obrázků v nové žádosti o zprávu

Důležité

Tato funkce služeb Azure Communication Services je aktuálně ve verzi Preview.

Rozhraní API a sady SDK verze Preview jsou poskytovány bez smlouvy o úrovni služeb. Doporučujeme je nepoužívat pro produkční úlohy. Některé funkce nemusí být podporované nebo můžou mít omezené možnosti.

Další informace najdete v dodatečných podmínkách použití pro verze Preview Microsoft Azure.

Kromě zpracování zpráv s vloženými obrázky poskytuje sada SDK chatu pro JavaScript také řešení, které umožňuje komunikačnímu uživateli posílat vložené obrázky uživateli Microsoft Teams v chatu interoperability.

Podívejte se na nové rozhraní API z ChatThreadClient:

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

Rozhraní API přebírá objekt blob obrázku, řetězec názvu souboru a zpětné volání funkce, které hlásí průběh nahrávání.

Pokud chcete poslat obrázek jinému účastníkovi chatu, musíte:

  1. Nahrajte obrázek přes uploadImage rozhraní API a ChatThreadClientuložte vrácený objekt.
  2. Vytvořte obsah zprávy a nastavte přílohu na vrácený objekt, který jste uložili v předchozím kroku.
  3. Odešlete novou zprávu prostřednictvím sendMessage rozhraní API z ChatThreadClient.

Vytvořte nový výběr souborů, který přijímá obrázky:

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

Teď nastavte naslouchací proces událostí, když dojde ke změně stavu:

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

Pokud se změní stav, musíte vytvořit novou funkci:

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

V tomto příkladu jste vytvořili obrázek, který FileReader bude číst jako base64obrázky s kódováním, a pak vytvoříte před Blob voláním rozhraní API ChatSDK, které je nahraje. Vytvořili jste globální uploadedImageModels objekt pro ukládání datových modelů nahraných obrázků ze sady SDK chatu.

Nakonec musíte upravit naslouchací sendMessageButton proces události, který jste vytvořili dříve, a připojit obrázky, které jste nahráli.

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

Hotovo. Teď spusťte kód, abyste ho viděli v akci.

Spuštění kódu

Uživatelé webpacku můžou použít webpack-dev-server k sestavení a spuštění aplikace. Spuštěním následujícího příkazu sbalte hostitele aplikace na místním webovém serveru:

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

Ukázka

Otevřete prohlížeč a přejděte na http://localhost:8080/. V poli pro odeslání máte nový oddíl pro připojení obrázků.

Snímek obrazovky znázorňující ukázkovou aplikaci s nově přidanou částí pro připojení obrázků

Dále můžete vybrat obrázky, které chcete připojit.

Snímek obrazovky znázorňující výběr souboru se seznamem obrázků, které můžou uživatelé připojit ke svým zprávám

Snímek obrazovky znázorňující ukázkovou aplikaci se dvěma připojenými obrázky

Uživatel Teams by teď měl dostat obrázek, který jste právě odeslali, když vyberou Možnost Odeslat.

Snímek obrazovky znázorňující ukázkovou aplikaci s odeslanou zprávou se dvěma vloženými obrázky

Snímek obrazovky znázorňující klienta Teams s přijatou zprávou se dvěma vloženými obrázky

V tomto kurzu se dozvíte, jak povolit podporu vložených imagí pomocí sady SDK chatu služeb Azure Communication Services pro jazyk C#.

V tomto kurzu se naučíte:

  • Zpracování vložených obrázků pro nové zprávy

Požadavky

Goal

  • previewUri Uchopte vlastnost pro vložené přílohy obrázků.

Zpracování vložených obrázků pro nové zprávy

V rychlém startu se budete dotazovat na zprávy a přidávat do vlastnosti nové zprávymessageList. Na této funkci se později stavíte, abyste zahrnuli analýzu a načítání vložených imagí.

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

Z příchozí události typu ChatMessageReceivedEvent, vlastnost pojmenovaná attachments obsahuje informace o vloženém obrázku. Stačí vykreslit vložené obrázky v uživatelském rozhraní.

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

Následující JSON je příkladem toho, co ChatAttachment by mohlo vypadat jako u přílohy obrázku:

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

Vraťte se zpět a nahraďte kód, který přidá další logiku pro parsování a načtení příloh obrázků:

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

V tomto příkladu vezmete všechny přílohy ze zprávy typu Image a pak načtete každý z obrázků. K autorizačním účelům musíte použít svoji Token část Bearer hlavičky požadavku. Po stažení obrázku ho můžete přiřadit k InlineImage prvku zobrazení.

Zahrnete také seznam identifikátorů URI přílohy, které se zobrazí spolu se zprávou v seznamu textových zpráv.

Ukázka

  • Spusťte aplikaci z integrovaného vývojového prostředí (IDE).
  • Zadejte odkaz na schůzku v Teams.
  • Připojte se ke schůzce.
  • Povolte uživatele na straně Teams.
  • Odešlete zprávu ze strany Teams s obrázkem.

Adresa URL, která je součástí zprávy, se zobrazí v seznamu zpráv. Poslední přijatý obrázek se vykreslí v dolní části okna.

Další kroky