次の方法で共有


チュートリアル: チャット アプリで添付ファイルのサポートを有効にする

Chat SDK は、会議のコンテキストで Microsoft Teams とシームレスに連携します。 Azure Communication Services ユーザーに添付ファイルを送信できるのは、Teams ユーザーだけです。 Azure Communication Services ユーザーは、Teams ユーザーに添付ファイルを送信できません。 現在の機能については、Teams とチャットの相互運用性に関する記事を参照してください。

添付ファイルのサポートを追加する

Chat SDK は、添付ファイルごとに previewUrl プロパティを提供します。 具体的には、previewUrl は SharePoint 上の Web ページへのリンクを提供します。これを通じ、アクセス許可があれば、ユーザーはファイルの内容の表示、ファイルの編集、ファイルのダウンロードを行うことができます。

この機能に関連する次のような制約があります。

  • 送信者のテナントの Teams 管理者は、この機能を制限するか完全に無効にするポリシーを適用できます。 たとえば、Teams 管理者は、特定のアクセス許可 (Anyone など) を無効にすることで、添付ファイルの URL (previewUrl) をアクセス不可にできます。

  • 現在、2 つのファイル アクセス許可のみがサポートされています。

    • Anyone
    • People you choose (メール アドレス付き)

    他のすべてのアクセス許可 (People in your organization など) がサポートされていないことを Teams ユーザーに周知します。 Teams ユーザーは Teams クライアントにファイルをアップロードした後、既定のアクセス許可がサポートされているかダブル チェックする必要があります。

  • 直接ダウンロード URL (url) はサポートされていません。

Chat SDK では、通常のファイル (fileAttachmentType) に加えて、imageAttachmentType プロパティも提供されます。 Azure Communication Services ユーザーは、Microsoft Teams クライアントが UI レイヤーで画像の添付ファイルをインライン画像に変換する方法の動作に似た方法で画像を添付できます。 詳細については、「画像の添付ファイルを処理する」を参照してください。

Azure Communication Services ユーザーは、[このデバイスからアップロード] を介して画像を追加できます。画像は Teams 側でレンダリングされ、Chat SDK はこのような 添付ファイルを image として返します。 [クラウド ファイルの添付] を介してアップロードされた画像の場合、画像は Teams 側で通常のファイルとして扱われ、Chat SDK はそのような添付ファイルを file として返します。

また、Azure Communication Services ユーザーは、ドラッグ アンド ドロップを使用するか、[このデバイスからアップロード] および [クラウド ファイルの添付] の添付ファイル メニュー コマンドを使用してのみファイルをアップロードできることに注意してください。 埋め込みメディアを含む特定の種類のメッセージ (ビデオ クリップ、オーディオ メッセージ、天気カードなど) は現在サポートされていません。

このチュートリアルでは、JavaScript 用 Azure Communication Services Chat SDK を使用して添付ファイルのサポートを有効にする方法を説明します。

サンプル コード

このチュートリアルの最終的なコードは GitHub にあります。

前提条件

目標

  • メッセージ スレッドで添付ファイルをレンダリングします。 各添付ファイル カードには、[開く] ボタンがあります。
  • 画像添付ファイルをインライン画像としてレンダリングします。

添付ファイルを処理する

JavaScript 用 Chat SDK は、通常の添付ファイルの file とメッセージのインライン画像の imageChatAttachmentType プロパティを返します。

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 の会議に参加させる」で作成したイベント ハンドラーに戻り、fileattachmentType プロパティを使用して添付ファイルを処理するための追加ロジックを追加します。

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 とスレッド ID を入力します。

  2. Teams クライアントから添付ファイルをいくつか送信します。

    3 つの添付ファイルを含むメッセージを送信した Teams クライアントを示すスクリーンショット。

  3. 新しいメッセージが添付ファイルと一緒にレンダリングされるのを確認できます。

    3 つの添付ファイルを含む受信メッセージを受信したサンプル アプリを示すスクリーンショット。

画像添付ファイルを処理する

画像の添付ファイルは、標準的な file の添付ファイルとは異なる方法で処理する必要があります。 画像の添付ファイルは imageattachmentType プロパティを持つため、プレビュー画像またはフルサイズの画像を取得するための通信トークンが必要です。

続行する前に、チャット アプリでインライン画像のサポートを有効にする方法を説明したチュートリアルを完了してください。 このチュートリアルでは、要求ヘッダーで通信トークンを必要とする画像をフェッチする方法について説明しています。 画像 BLOB を受け取ったら、この BLOB を指す ObjectUrl プロパティを作成する必要があります。 その後、その 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. サンプル アプリに戻り、同じ画像がレンダリングされていることを確認します。

    1 つのインライン画像がレンダリングされた受信メッセージを表示するサンプル アプリを示すスクリーンショット。

このチュートリアルでは、C# 用 Azure Communication Services Chat SDK を使用して添付ファイルのサポートを有効にする方法を説明します。

このチュートリアルでは、次の作業を行う方法について説明します。

  • 添付ファイルを処理します。
  • 画像の添付ファイルを処理します。

前提条件

サンプル コード

このチュートリアルの最終的なコードは GitHub にあります。

添付ファイルを処理する

C# 用 Chat SDK は、通常の添付ファイルの file とインライン画像の imageChatAttachmentType プロパティを返します。

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

ここで、前のクイックスタートで作成したイベント ハンドラーに戻り、 fileChatAttachmentType プロパティを使用して添付ファイルを処理するための追加ロジックを追加します。


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 添付ファイルとは異なる方法で処理する必要があります。 画像の添付ファイルは imageChatAttachmentType プロパティを持つため、プレビュー画像またはフルサイズの画像を取得するための通信トークンが必要です。

続行する前に、「インライン画像のサポートを有効にする」チュートリアルを完了してください。 画像の添付ファイルを識別するには、メッセージの内容に添付ファイルと同じ画像 ID が含まれているかどうかを確認する必要があります。

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

これで、アプリで画像の添付ファイルがサポートされるようになりました。

次のステップ