Tutorial: Habilitar la compatibilidad con imágenes insertadas en la aplicación de chat
El SDK de chat está diseñado para trabajar con Microsoft Teams sin problemas. En concreto, el SDK de chat proporciona una solución para recibir imágenes insertadas y enviar imágenes insertadas a los usuarios de Microsoft Teams.
En este tutorial, aprenderá a habilitar la compatibilidad con imágenes insertadas mediante el SDK de Azure Communication Services Chat para JavaScript.
Las imágenes insertadas son imágenes que se copian y pegan directamente en el cuadro de envío del cliente de Teams. Para las imágenes cargadas a través del menú Cargar desde este dispositivo o mediante arrastrar y colocar, como imágenes arrastradas directamente al cuadro de envío en Teams, debe hacer referencia a este tutorial como parte de la característica de uso compartido de archivos. (Vea la sección "Controlar datos adjuntos de imágenes").
Para copiar una imagen, los usuarios de Teams tienen dos opciones:
- Use el menú contextual del sistema operativo para copiar el archivo de imagen y péguelo en el cuadro de envío de su cliente de Teams.
- Usar métodos abreviados de teclado.
En este tutorial, aprenderá lo que necesita hacer cuando:
Nota:
La capacidad de enviar imágenes insertadas está disponible actualmente en versión preliminar pública. Solo está disponible para JavaScript. Para recibir imágenes insertadas, actualmente está disponible con carácter general. Está disponible para JavaScript y C# en un chat de interoperabilidad de Teams.
Requisitos previos
- Revise el inicio rápido Unirse a la aplicación chat en una reunión de Teams.
- Cree un recurso de Azure Communication Services. Para más información, consulte Creación de un recurso de Azure Communication Services. Debe registrar la cadena de conexión para este tutorial.
- Configure una reunión de Teams con su cuenta empresarial y tenga lista la dirección URL de la reunión.
- Use el SDK de chat para JavaScript (@azure/communication-chat) 1.4.0 o la versión más reciente. Para más información, vea Biblioteca cliente de Chat de comunicación de Azure para JavaScript.
Código de ejemplo
Encuentre el código finalizado de este tutorial en GitHub.
Control de imágenes insertadas recibidas en un nuevo evento de mensaje
En esta sección, aprenderá a representar imágenes insertadas en el contenido del mensaje insertado de un nuevo evento recibido.
En el inicio rápido, creó un controlador de eventos para el evento chatMessageReceived
, que se desencadena cuando recibe un nuevo mensaje del usuario de Teams. También anexa contenido de mensaje entrante a messageContainer
directamente después de recibir el chatMessageReceived
evento del chatClient
, de la siguiente manera:
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;
}
Desde el evento entrante de tipo ChatMessageReceivedEvent
, una propiedad denominada attachments
contiene información sobre la imagen insertada. Es todo lo que necesita para representar imágenes insertadas en la interfaz de usuario:
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";
Ahora vuelva al código anterior para agregar cierta lógica adicional, como los fragmentos de código siguientes:
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;
});
}
En este ejemplo, ha creado dos funciones auxiliares, fetchPreviewImages
y setImgHandler
. La primera captura la imagen de vista previa directamente de la previewURL
proporcionada en cada objeto ChatAttachment
con un encabezado de autenticación. Del mismo modo, configurará un evento de onclick
para cada imagen de la función setImgHandler
. En el controlador de eventos, se captura una imagen a escala completa de la propiedad url
del objeto ChatAttachment
con un encabezado de autenticación.
Ahora debe exponer el token en el nivel global porque necesita construir un encabezado de autenticación con él. Debe modificar el código siguiente:
// 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;
...
}
Para mostrar la imagen a escala completa en una superposición, también debe agregar un nuevo componente:
<div class="overlay" id="overlay-container">
<div class="content">
<img id="full-scale-image" src="" alt="" />
</div>
</div>
Con algunas hojas de estilo 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
}
Ahora que tiene configurada una superposición, es el momento de trabajar en la lógica para representar imágenes a escala completa. Recuerde que ha creado un controlador de eventos onClick
para llamar a una función 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';
}
Una última cosa que desea agregar es la capacidad de descartar la superposición cuando se hace clic en la imagen:
loadingImageOverlay.addEventListener('click', () => {
overlayContainer.style.display = 'none';
});
Ahora ha realizado todos los cambios que necesita para representar imágenes insertadas para los mensajes que proceden de notificaciones en tiempo real.
Ejecución del código
Los usuarios de Webpack pueden usar webpack-dev-server
para compilar y ejecutar la aplicación. Ejecute el siguiente comando para agrupar el host de la aplicación en un servidor web local:
npx webpack-dev-server --entry ./client.js --output bundle.js --debug --devtool inline-source-map
Demostración
Abra su explorador y diríjase a http://localhost:8080/
. Escriba la dirección URL de la reunión y el identificador de la conversación. Envíe algunas imágenes insertadas desde el cliente de Teams.
A continuación, debería ver el nuevo mensaje representado junto con las imágenes de vista previa.
Una vez que el usuario de Azure Communication Services selecciona la imagen de vista previa, aparece una superposición con la imagen de escala completa enviada por el usuario de Teams.
Controlar el envío de imágenes insertadas en una nueva solicitud de mensaje
Importante
Esta característica de Azure Communication Services se encuentra actualmente en versión preliminar.
Las API y los SDK en versión preliminar se proporcionan sin contrato de nivel de servicio. Se recomienda no usarlos para las cargas de trabajo de producción. Es posible que algunas características no sean compatibles o que sus funcionalidades estén limitadas.
Para obtener más información, consulte Términos de uso complementarios para las Versiones preliminares de Microsoft Azure.
Además de controlar mensajes con imágenes insertadas, el SDK de Chat para JavaScript también proporciona una solución para permitir que el usuario de comunicación envíe imágenes insertadas al usuario de Microsoft Teams en un chat de interoperabilidad.
Eche un vistazo a la nueva API de ChatThreadClient
:
var imageAttachment = await chatThreadClient.uploadImage(blob, file.name, {
"onUploadProgress": reportProgressCallback
});
La API toma un blob de imagen, una cadena de nombre de archivo y una devolución de llamada de función que informa del progreso de la carga.
Para enviar una imagen a otro participante de chat, debe:
- Cargue la imagen a través de la
uploadImage
API desdeChatThreadClient
, y guarde el objeto devuelto. - Redacte el contenido del mensaje y establezca un archivo adjunto en el objeto devuelto que guardó en el paso anterior.
- Envíe el nuevo mensaje a través de la
sendMessage
API desdeChatThreadClient
.
Cree un nuevo selector de archivos que acepte imágenes:
<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>
Ahora, configure un agente de escucha de eventos para cuando se produzca un cambio de estado:
document.getElementById("upload").addEventListener("change", uploadImages);
Debe crear una nueva función para cuando cambie el estado:
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);
}
En este ejemplo, ha creado un FileReader
para leer cada imagen como imágenes codificadas base64
y, a continuación, crear un Blob
antes de llamar a la API ChatSDK para cargarlas. Ha creado un global uploadedImageModels
para guardar los modelos de datos de imágenes cargadas desde el SDK de Chat.
Por último, debe modificar el agente de escucha de eventos sendMessageButton
que creó anteriormente para adjuntar las imágenes que cargó.
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}`);
});
Eso es. Ahora ejecute el código para verlo en acción.
Ejecución del código
Los usuarios de Webpack pueden usar webpack-dev-server
para compilar y ejecutar la aplicación. Ejecute el siguiente comando para agrupar el host de la aplicación en un servidor web local:
npx webpack-dev-server --entry ./client.js --output bundle.js --debug --devtool inline-source-map
Demostración
Abra su explorador y diríjase a http://localhost:8080/
. Tiene una nueva sección en el cuadro de envío para adjuntar imágenes.
A continuación, puede seleccionar las imágenes que desea adjuntar.
El usuario de Teams ahora debería recibir la imagen que acaba de enviar al seleccionar Enviar.
En este tutorial se muestra cómo habilitar la compatibilidad con imágenes insertadas mediante el SDK de Azure Communication Services Chat para C#.
En este tutorial, aprenderá a:
- Controle las imágenes insertadas para los nuevos mensajes.
Requisitos previos
- Revise el inicio rápido Unirse a la aplicación de chat en una reunión de Teams.
- Cree un recurso de Azure Communication Services. Para más información, consulte Creación de un recurso de Azure Communication Services. Debe registrar la cadena de conexión para este tutorial.
- Configure una reunión de Teams con su cuenta empresarial y tenga lista la dirección URL de la reunión.
- Use el SDK de chat para C# (Azure.Communication.Chat) 1.3.0 o la versión más reciente. Para más información, vea Biblioteca cliente de Chat de comunicación de Azure para .NET.
Objetivo
- Tome la propiedad
previewUri
para los datos adjuntos de imagen insertados.
Control de imágenes insertadas para mensajes nuevos
En el inicio rápido, sondea mensajes y anexa nuevos mensajes a la propiedad messageList
. Más adelante, se basa en esta funcionalidad para incluir el análisis y la captura de las imágenes insertadas.
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}");
}
}
Desde el evento entrante de tipo ChatMessageReceivedEvent
, la propiedad denominada attachments
contiene información sobre la imagen insertada. Es todo lo que necesita para representar imágenes insertadas en la interfaz de usuario.
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 }
}
El siguiente JSON es un ejemplo de lo que ChatAttachment
podría tener un aspecto para los datos adjuntos de una imagen:
"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"
}
]
Ahora vuelva y reemplace el código para agregar lógica adicional para analizar y capturar los datos adjuntos de la imagen:
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}");
}
En este ejemplo, obtendrá todos los datos adjuntos del mensaje de tipo Image
y, a continuación, capturará cada una de las imágenes. Debe usar el Token
en la parte Bearer
del encabezado de solicitud con fines de autorización. Una vez descargada la imagen, puede asignarla al elemento InlineImage
de la vista.
También se incluye una lista de los URI de datos adjuntos que se mostrarán junto con el mensaje de la lista de mensajes de texto.
Demostración
- Ejecute la aplicación desde el entorno de desarrollo integrado (IDE).
- Siga el vínculo a una reunión de Teams.
- Unirse a la reunión.
- Admita el usuario en el lado de Teams.
- Envíe un mensaje desde Teams con una imagen.
La dirección URL incluida con el mensaje aparece en la lista de mensajes. La última imagen recibida se representa en la parte inferior de la ventana.
Pasos siguientes
- Obtenga más información sobre otras características de interoperabilidad admitidas.
- Consulte nuestro ejemplo de héroe de chat.
- Obtenga más información sobre cómo funciona chat.