共用方式為


教學課程:在聊天應用程式中啟用檔案附件支援

聊天 SDK 可在會議內容中與 Microsoft Teams 順暢地運作。 只有 Teams 使用者可以將檔案附件傳送給 Azure 通訊服務 使用者。 Azure 通訊服務 用戶無法傳送檔案附件給Teams使用者。 如需目前的功能,請參閱 Teams Interop Chat

新增檔案附件支援

Chat SDK 會為每個檔案附件提供 previewUrl 屬性。 具體來說,連結到 SharePoint 上的網頁, previewUrl 用戶可以在其中查看檔案的內容、編輯檔案,以及如果許可權允許,請下載檔案。

某些條件約束與這項功能相關聯:

  • 寄件者租使用者的 Teams 系統管理員可能會強加完全限制或停用此功能的原則。 例如,Teams 系統管理員可以停用某些許可權(例如 Anyone),可能會導致檔案附件 URL (previewUrl) 無法存取。

  • 我們目前僅支援兩個檔案許可權:

    • Anyone
    • People you choose (電子郵件地址)

    讓您的 Teams 使用者知道不支援所有其他許可權(例如 People in your organization)。 您的 Teams 使用者應該仔細檢查,以確保在 Teams 用戶端上上傳檔案之後,支援默認許可權。

  • 不支援直接下載 URL (url)。

除了一般檔案 (搭配 AttachmentTypefile),Chat SDK 也提供 AttachmentTypeimage屬性。 Azure 通訊服務 使用者可以以反映 Microsoft Teams 用戶端如何將影像附件轉換成 UI 層內嵌影像的行為來附加影像。 如需詳細資訊,請參閱 處理影像附件

Azure 通訊服務 使用者可以透過 新增影像從此裝置上傳,這會在 Teams 端轉譯,而 Chat SDK 會傳回這類附件,例如 image。 對於透過 附加雲端檔案上傳的影像,影像會被視為 Teams 端的一般檔案,讓 Chat SDK 傳回這類附件,例如 file

另請注意,Azure 通訊服務 使用者只能使用拖放或透過附件功能表命令上傳此裝置附加雲端檔案來上傳檔案。 目前不支援內嵌媒體的特定訊息類型(例如視訊剪輯、音訊訊息和天氣卡片)。

本教學課程說明如何使用 Azure 通訊服務 Chat SDK for JavaScript 來啟用檔案附件支援。

範例指令碼

GitHub 上尋找本教學課程的完成程式碼。

必要條件

  • 檢閱將聊天應用程式加入Teams會議的快速入門
  • 建立 Azure 通訊服務資源。 如需詳細資訊,請參閱建立 Azure 通訊服務資源。 您必須記錄此教學課程的連接字串
  • 使用您的商務帳戶設定 Teams 會議,並備妥會議 URL。
  • 使用 Chat SDK for JavaScript (@azure/communication-chat) 1.5.0 或最新版本。 如需詳細資訊,請參閱 適用於 JavaScript 的 Azure 通訊聊天用戶端連結庫。

目標

  • 在訊息線程中轉譯檔案附件。 每個檔案附件卡片都有 [ 開啟 ] 按鈕。
  • 將影像附件轉譯為內嵌影像。

處理檔案附件

適用於 JavaScript 的 Chat SDK 會針對一ChatAttachmentType般檔案附件和image訊息內嵌影像傳回 的屬性file

export interface ChatMessageReceivedEvent extends BaseChatMessageEvent {
  /**
   * Content of the message.
   */
  message: string;

  /**
   * Chat message attachment.
   */
  attachments?: ChatAttachment[];
  
  ...
}

export interface ChatAttachment {
  /** Id of the attachment */
  id: string;
  /** The type of attachment. */
  attachmentType: AttachmentType;
  /** The name of the attachment content. */
  name?: string;
  /** The URL that is used to provide the original size of the inline images */
  url?: string;
  /** The URL that provides the preview of the attachment */
  previewUrl?: string;
}

/** Type of supported attachments. */
export type ChatAttachmentType = "image" | "file" | "unknown";

例如,下列 JSON 會顯示 ChatAttachment 影像附件和檔案附件的外觀:

"attachments": [
    {
        "id": "08a182fe-0b29-443e-8d7f-8896bc1908a2",
        "attachmentType": "file",
        "name": "business report.pdf",
        "previewUrl": "https://contoso.sharepoint.com/:u:/g/user/h8jTwB0Zl1AY"
    },
    {
        "id": "9d89acb2-c4e4-4cab-b94a-7c12a61afe30",
        "attachmentType": "image", 
        "name": "Screenshot.png",
        "url": "https://contoso.communication.azure.com/chat/threads/19:9d89acb29d89acb2@thread.v2/messages/123/images/9d89acb2-c4e4-4cab-b94a-7c12a61afe30/views/original?api-version=2023-11-15-preview",
        "previewUrl": "https://contoso.communication.azure.com/chat/threads/19:9d89acb29d89acb2@thread.v2/messages/123/images/9d89acb2-c4e4-4cab-b94a-7c12a61afe30/views/small?api-version=2023-11-15-preview"
      }
]

現在,讓我們回到您在快速入門:將聊天應用程式加入Teams會議建立的事件處理程式,並新增一些額外的邏輯來處理具有 屬性的attachmentTypefile附件:

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);
  } else {
    renderSentMessage(e.message);
  }
});

async function renderReceivedMessage(event) {
    messages += `<div class="container lighter"> ${event.message} </div>`;
    messagesContainer.innerHTML = messages;

    // Get the list of attachments and calls renderFileAttachments to construct a file attachment card
    var attachmentHtml = event.attachments
        .filter(attachment => attachment.attachmentType === "file")
        .map(attachment => renderFileAttachments(attachment))
        .join('');
    messagesContainer.innerHTML += attachmentHtml;
}

function renderFileAttachments(attachment) {
    var re = /(?:\.([^.]+))?$/;
    var fileExtension = re.exec(attachment.name)[1];  
    return '<div class="attachment-container">' +
        '<img class="attachment-icon" alt="attachment file icon" />' +
        '<div>' +
        '<p class="attachment-type">' + fileExtension + '</p>' +
        '<p>' + attachment.name + '</p>' +
        '<a href=' + attachment.previewUrl + ' target="_blank" rel="noreferrer">Open</a>' +
        '</div>' +
        '</div>';
}

請務必為附件卡片新增一些 CSS:

  /* Let's make the chat popup scrollable */
  .chat-popup {

     ...

     max-height: 650px;
     overflow-y: scroll;
}

 .attachment-container {
     overflow: hidden;
     background: #f3f2f1;
     padding: 20px;
     margin: 0;
     border-radius: 10px;
}
 .attachment-container img {
     width: 50px;
     height: 50px;
     float: left;
     margin: 0;
}
 .attachment-container p {
     font-weight: 700;
     margin: 0 5px 20px 0;
}
 .attachment-container {
     display: grid;
     grid-template-columns: 100px 1fr;
     margin-top: 5px;
}
 .attachment-icon {
     content: url("data:image/svg+xml;base64, ...");
}
 .attachment-container a {
     background-color: #dadada;
     color: black;
     font-size: 12px;
     padding: 10px;
     border: none;
     cursor: pointer;
     border-radius: 5px;
     text-align: center;
     margin-right: 10px;
     text-decoration: none;
     margin-top: 10px;
}
 .attachment-container a:hover {
     background-color: black;
     color: white;
}
 .attachment-type {
     position: absolute;
     color: black;
     border: 2px solid black;
     background-color: white;
     margin-top: 50px;
     font-family: sans-serif;
     font-weight: 400;
     padding: 2px;
     text-transform: uppercase;
     font-size: 8px;
}

這是處理檔案附件所需的所有專案。 接下來,讓我們執行程序代碼。

執行程式碼

針對 Webpack,您可以使用 webpack-dev-server 屬性來建置和執行您的應用程式。 執行下列命令,將應用程式主機組合在本機 Web 伺服器上:

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

或:

npm start

檔案附件示範

  1. 開啟瀏覽器,並前往 http://localhost:8080/。 輸入會議 URL 和執行緒識別碼。

  2. 從 Teams 用戶端傳送一些檔案附件。

    顯示 Teams 用戶端的螢幕快照,其中包含三個檔案附件的已傳送訊息。

  3. 您應該會看到正在轉譯的新訊息以及檔案附件。

    此螢幕快照顯示包含三個檔案附件之已接收內送訊息的範例應用程式。

處理影像附件

影像附件的處理方式與標準 file 附件不同。 影像附件具有 attachmentTypeimage屬性,需要通訊令牌才能擷取預覽或完整大小的影像。

繼續之前,請先完成示範 如何在聊天應用程式中啟用內嵌影像支援的教學課程。 本教學課程說明如何擷取要求標頭中需要通訊令牌的映像。 收到映射 Blob 之後,您必須建立 ObjectUrl 指向此 Blob 的屬性。 然後將此 URL src 插入每個內嵌影像的屬性。

現在您已熟悉內嵌影像的運作方式,您可以轉譯影像附件,例如一般內嵌影像。

首先,每當有影像附件時,將 image 標籤插入郵件內容:

async function renderReceivedMessage(event) {
    messages += `<div class="container lighter"> ${event.message} </div>`;
    messagesContainer.innerHTML = messages;
    console.log(event);
    // Filter out inline images from attachments
    const imageAttachments = event.attachments?.filter(
        (attachment) =>
        attachment.attachmentType === "image" && !messages.includes(attachment.id)
    );
    // Inject image tag for all image attachments
    var imageAttachmentHtml =
        imageAttachments
        .map((attachment) => renderImageAttachments(attachment))
        .join("") ?? "";
    messagesContainer.innerHTML += imageAttachmentHtml;

    // Get list of attachments and calls renderFileAttachments to construct a file attachment card
    var attachmentHtml =
        event.attachments
        ?.filter((attachment) => attachment.attachmentType === "file")
        .map((attachment) => renderFileAttachments(attachment))
        .join("") ?? "";
    messagesContainer.innerHTML += attachmentHtml;

    // Fetch and render preview images
    fetchPreviewImages(imageAttachments);
}

function renderImageAttachments(attachment) {
    return `<img alt="image" src="" itemscope="png" id="${attachment.id}" style="max-width: 100px">`
}

現在,讓我們從fetchPreviewImages()教學課程:啟用內嵌影像支援,並如實使用,而不需要進行任何變更:

function fetchPreviewImages(attachments) {
    if (!attachments.length > 0) {
        return;
    }
    Promise.all(
        attachments.map(async (attachment) => {
            const response = await fetch(attachment.previewUrl, {
                method: 'GET',
                headers: {
                    'Authorization': 'Bearer ' + tokenString,
                },
            });
            return {
                id: attachment.id,
                content: await response.blob(),
            };
        }),
    ).then((result) => {
        result.forEach((imageRef) => {
            const urlCreator = window.URL || window.webkitURL;
            const url = urlCreator.createObjectURL(imageRef.content);
            document.getElementById(imageRef.id).src = url;
        });
    }).catch((e) => {
        console.log('error fetching preview images');
    });
}

此函式需要 tokenString 屬性,因此您需要在 中 init()初始化的全域複本,如下列代碼段所示:

var tokenString = '';

async function init() {
    ...
    const {
        token,
        expiresOn
    } = tokenResponse;
    
    tokenString = token;
    ...
}

現在您有影像附件支援。 繼續執行程序代碼,並查看其運作情形。

影像附件示範

  1. 從 Teams 用戶端傳送一些影像附件。

    顯示 Teams 用戶端的螢幕快照,其中顯示上傳影像附件的傳送方塊。

  2. 傳送影像附件之後,請注意它會變成Teams用戶端上的內嵌影像。

    顯示 Teams 用戶端的螢幕快照,其中顯示已傳送給其他參與者之影像附件的訊息。

  3. 返回範例應用程式,並確定已轉譯相同的影像。

    顯示範例應用程式的螢幕快照,其中顯示已轉譯一個內嵌影像的內送訊息。

本教學課程說明如何使用 Azure 通訊服務 Chat SDK for C# 來啟用檔案附件支援。

在本教學課程中,您會了解如何:

  • 處理檔案附件。
  • 處理影像附件。

必要條件

  • 檢閱將聊天應用程式加入Teams會議的快速入門
  • 建立 Azure 通訊服務 資源,如建立 Azure 通訊服務 資源中所述。 您必須記錄此教學課程的連接字串
  • 使用您的商務帳戶設定 Teams 會議,並備妥會議 URL。
  • 下載適用於 C# 的聊天 SDK (@azure/communication-chat) 1.3.0 或最新版本。 如需詳細資訊,請參閱 Azure 通訊聊天用戶端連結庫

範例指令碼

GitHub 尋找本教學課程的完成程式碼。

處理檔案附件

適用於 C# 的 Chat SDK 會針對一ChatAttachmentType般檔案附件和image內嵌影像傳回 的屬性file

public readonly partial struct ChatAttachmentType : IEquatable<ChatAttachmentType>
{
        private const string ImageValue = "image";
        private const string FileValue = "file";
        /// <summary> image. </summary>
        public static ChatAttachmentType Image { get; } = new ChatAttachmentType(ImageValue);
        /// <summary> file. </summary>
        public static ChatAttachmentType File { get; } = new ChatAttachmentType(FileValue);
}


例如,當您從伺服器端收到要求時,下列 JSON 會顯示 ChatAttachment 影像附件和檔案附件的外觀:

"attachments": [
    {
        "id": "08a182fe-0b29-443e-8d7f-8896bc1908a2",
        "attachmentType": "file",
        "name": "business report.pdf",
        "previewUrl": "https://contoso.sharepoint.com/:u:/g/user/h8jTwB0Zl1AY"
    },
    {
        "id": "9d89acb2-c4e4-4cab-b94a-7c12a61afe30",
        "attachmentType": "image", 
        "name": "Screenshot.png",
        "url": "https://contoso.communication.azure.com/chat/threads/19:9d89acb29d89acb2@thread.v2/messages/123/images/9d89acb2-c4e4-4cab-b94a-7c12a61afe30/views/original?api-version=2023-11-15-preview",
        "previewUrl": "https://contoso.communication.azure.com/chat/threads/19:9d89acb29d89acb2@thread.v2/messages/123/images/9d89acb2-c4e4-4cab-b94a-7c12a61afe30/views/small?api-version=2023-11-15-preview"
      }
]

現在回到您在上一個快速入門中建立的事件處理程式,並新增一些額外的邏輯來處理具有 屬性的ChatAttachmentTypefile附件:


await foreach (ChatMessage message in allMessages)
{
    // Get message attachments that are of type 'file'
    IEnumerable<ChatAttachment> fileAttachments = message.Content.Attachments.Where(x => x.AttachmentType == ChatAttachmentType.File);
    var chatAttachmentFileUris = new List<Uri>();
    foreach (var file in fileAttachments) 
    {
        chatAttachmentFileUris.Add(file.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 = fileAttachments.Count() > 0 ? "[Attachments]:\n" + string.Join(",\n", chatAttachmentFileUris) : "";
        messageList.Add(long.Parse(message.SequenceId), $"{userPrefix}{strippedMessage}\n{chatAttachments}");
    }
}

具體而言,針對每個檔案附件,您會取得 previewUrl 屬性,並在 中 for loop建構 URL 清單。 接著,您會內嵌字串以及聊天訊息內容。

處理影像附件

您需要以不同於標準 file 附件的方式處理影像附件。 影像附件具有 ChatAttachmentTypeimage屬性,需要通訊令牌才能擷取預覽或完整大小的影像。

繼續之前,請先完成 啟用內嵌影像支援 教學課程。 若要識別影像附件,您必須瞭解郵件內容是否包含附件中的相同影像標識碼。

bool isImageAttachment = message.Content.Message.Contains(x.Id);

如果此旗標為 true,則您會套用內嵌影像邏輯來轉譯它:

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

現在您的應用程式支援影像附件。

下一步