你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
教程:在聊天应用中启用文件附件支持
聊天 SDK 在会议上下文中与 Microsoft Teams 无缝配合工作。 只有 Teams 用户才能将文件附件发送给 Azure 通信服务用户。 Azure 通信服务用户无法向 Teams 用户发送文件附件。 有关当前功能,请参阅 Teams 互操作聊天。
添加文件附件支持
聊天 SDK 为每个文件附件提供 previewUrl
属性。 尤其是,previewUrl
链接到 SharePoint 上的网页,用户可在该网页上查看文件内容、编辑文件并下载文件(如果权限允许)。
此功能有一些约束条件:
发件人租户的 Teams 管理员可以实施完全限制或禁用此功能的策略。 例如,Teams 管理员可以禁用某些权限(例如
Anyone
),这会导致文件附件 URL (previewUrl
) 不可访问。我们目前仅支持两种文件权限:
Anyone
People you choose
(使用电子邮件地址选择)
让 Teams 用户知道,所有其他权限(例如,
People in your organization
)均不受支持。 Teams 用户应仔细检查,确保在 Teams 客户端上上传文件后支持默认权限。不支持直接下载 URL (
url
)。
除了常规文件(file
为 AttachmentType
),聊天 SDK 还提供 AttachmentType
的 image
属性。 Azure 通信服务用户可以这样附加图像,即反映了 Microsoft Teams 客户端如何在 UI 层将图像附件转换内联图像的行为。 有关详细信息,请参阅处理图像附件。
Azure 通信服务用户可通过“从此设备上传”(可在 Teams 端找到)来添加图像,聊天 SDK 会返回 image
等附件。 通过“附加云文件”上传的图像会在 Teams 端被视为常规文件,因此聊天 SDK 会将此类附件返回为 file
。
另请注意,Azure 通信服务用户只能通过拖放来上传文件,或者通过“从此设备上传”和“附加云文件”这两个附件菜单命令进行上传。 目前不支持某些包含嵌入式媒体(例如视频剪辑、音频消息、天气卡)的消息类型。
本教程介绍如何使用用于 JavaScript 的 Azure 通信服务聊天 SDK 启用文件附件支持。
代码示例
在 GitHub 上找到本教程的最终代码。
先决条件
- 查看快速入门:将聊天应用加入 Teams 会议。
- 创建 Azure 通信服务资源。 有关详细信息,请参阅创建 Azure 通信服务资源。 需要为此教程记录连接字符串。
- 使用企业帐户设置 Teams 会议,并准备好会议 URL。
- 使用适用于 JavaScript 的聊天 SDK (@azure/communication-chat) 1.5.0 或最新版本。 有关详细信息,请参阅适用于 JavaScript 的 Azure 通信聊天客户端库。
目标
- 在消息会话中呈现文件附件。 每个文件附件卡都有一个“打开”按钮。
- 将图像附件呈现为内联图像。
处理文件附件
对于常规文件附件,用于 JavaScript 的聊天 SDK 会返回 file
作为 ChatAttachmentType
,而对于消息内联图像,则返回 image
。
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 会议中创建的事件处理程序,并添加一些额外的逻辑来处理 attachmentType
属性为 file
的附件:
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
文件附件演示
打开浏览器并转到
http://localhost:8080/
。 输入会议 URL 和会话 ID。从 Teams 客户端发送一些文件附件。
应会看到正在呈现的新消息以及文件附件。
处理图像附件
图像附件需要与标准 file
附件采用不同的处理方式。 图像附件的 image
属性为 attachmentType
,这要求有通信令牌才能检索预览图像或全尺寸图像。
在继续之前,请完成演示如何在聊天应用中启用内联图像支持的教程。 此教程介绍如何提取请求标中需要通信令牌的图像。 收到图像 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;
...
}
现在,你已获得图像附件支持。 继续运行代码,亲眼看看效果。
图像附件演示
从 Teams 客户端发送一些图像附件。
发送图像附件后,请注意,它在 Teams 客户端变成了内联图像。
返回到示例应用,并确保呈现相同的图像。
本教程介绍如何使用用于 C# 的 Azure 通信服务聊天 SDK 启用文件附件支持。
本教程介绍如何执行下列操作:
- 处理文件附件。
- 处理图像附件。
先决条件
- 查看快速入门:将聊天应用加入 Teams 会议。
- 如创建 Azure 通信服务资源所述,创建一个 Azure 通信服务资源。 需要为此教程记录连接字符串。
- 使用企业帐户设置 Teams 会议,并准备好会议 URL。
- 下载用于 C# 的聊天 SDK (@azure/communication-chat) 1.3.0 或最新版本。 有关详细信息,请参阅 Azure 通信聊天客户端库。
示例代码
在 GitHub 上找到本教程的最终代码。
处理文件附件
对于常规文件附件,用于 C# 的聊天 SDK 返回的 ChatAttachmentType
属性为 file
,而对于内联图像,则返回 image
。
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"
}
]
接下来,返回到之前在快速入门中创建的事件处理程序,并添加一些额外的逻辑来处理 ChatAttachmentType
属性为 file
的附件:
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
附件不同的方式处理图像附件。 图像附件的 ChatAttachmentType
属性为 image
,这要求有通信令牌才能检索预览图像或全尺寸图像。
在继续之前,请完成启用内联映像支持教程。 为了标识图像附件,需要确定消息内容是否包含附件中的相同图像 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);
}
现在,应用支持图像附件。
后续步骤
- 详细了解如何启用内联图像支持。
- 详细了解其他受支持的互操作性功能。
- 查看我们的出色聊天示例。
- 详细了解聊天的工作原理。