Compartir vía


Tutorial: Habilitación de la compatibilidad con archivos adjuntos en la aplicación Chat

El SDK de Chat funciona sin problemas con Microsoft Teams en contextos de reunión. Solo los usuarios de Teams pueden enviar datos adjuntos de archivos a los usuarios de Azure Communication Services. Los usuarios de Azure Communication Services no pueden enviar datos adjuntos de archivos a los usuarios de Teams. Para conocer las funcionalidades actuales, consulte Chat de interoperabilidad de Teams.

Adición de compatibilidad con archivos adjuntos

El SDK de chat proporciona la propiedad previewUrl para cada archivo adjunto. De forma específica, previewUrl vincula a una página web en SharePoint donde el usuario puede ver el contenido del archivo, editarlo y descargarlo si los permisos lo permiten.

Esta característica tiene asociadas algunas restricciones:

  • El administrador de Teams del inquilino del remitente podría imponer directivas que limiten o deshabiliten completamente esta característica. Por ejemplo, el administrador de Teams podría deshabilitar determinados permisos (como Anyone) que podrían hacer que la URL de los archivos adjuntos (previewUrl) no fuera accesible.

  • Actualmente solo se admiten dos permisos de archivo:

    • Anyone
    • People you choose (con dirección de correo electrónico)

    Informe a los usuarios de Teams de que no se admiten todos los demás permisos (como People in your organization). Los usuarios de Teams deberían comprobarlo para asegurarse de que se admita el permiso predeterminado después de cargar el archivo en el cliente de Teams.

  • No se admite la URL de descarga directa (url).

Además de los archivos normales (con AttachmentType de file), el SDK de Chat también proporciona la propiedad AttachmentType de image. Los usuarios de Azure Communication Services pueden adjuntar imágenes de forma que reflejen el comportamiento de cómo el cliente de Microsoft Teams convierte los datos adjuntos de imagen en imágenes insertadas en la capa de interfaz de usuario. Para obtener más información, consulte Controlar datos adjuntos de imagen.

Los usuarios de Azure Communication Services pueden agregar imágenes a través de Cargar desde este dispositivo, que se representa en el lado de Teams y el SDK de Chat devuelve tales datos adjuntos como image. En el caso de imágenes cargadas a través de Adjuntar archivos en la nube, las imágenes se tratarán como archivos normales en Teams, por lo que el SDK de Chat devolverá tales datos adjuntos como file.

Tenga en cuenta también que los usuarios de Azure Communication Services solo podrán cargar archivos mediante arrastrar y colocar, o bien a través de los comandos de menú de datos adjuntos Cargar desde este dispositivo y Adjuntar archivos en la nube. Actualmente, no se admiten determinados tipos de mensajes con medios insertados (como clips de vídeo, mensajes de audio y tarjetas meteorológicas).

En este tutorial se describe cómo habilitar la compatibilidad con archivos insertados mediante el SDK de Azure Communication Services Chat para JavaScript.

Código de ejemplo

Encuentre el código finalizado de este tutorial en GitHub.

Requisitos previos

Objetivos

  • Representar los datos adjuntos del archivo en el subproceso del mensaje. Cada tarjeta de datos adjuntos de archivo tiene un botón Abrir.
  • Representar datos adjuntos de imágenes como imágenes insertadas.

Controlar archivos adjuntos

El SDK de Chat para JavaScript devuelve una propiedad ChatAttachmentType de file para datos adjuntos de archivos normales y image para imágenes insertadas con mensajes.

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

Por ejemplo, el siguiente JSON muestra cómo ChatAttachment podría mostrar los datos adjuntos de imagen y los datos adjuntos de un archivo:

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

Ahora volveremos al controlador de eventos que creó en Inicio rápido: unir la aplicación Chat a una reunión de Teams y agregaremos cierta lógica adicional para manipular los datos adjuntos con la propiedad attachmentType de 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>';
}

Asegúrese de agregar algunas CSS para la tarjeta de datos adjuntos:

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

Es todo lo que necesita para controlar los datos adjuntos de archivos. A continuación, ejecutemos el código.

Ejecución del código

En cuanto a WebPack, use la propiedad 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

O:

npm start

Demostración de archivos adjuntos

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

  2. Envíe algunos datos adjuntos de archivo desde el cliente de Teams.

    Captura de pantalla que muestra el cliente de Teams con un mensaje enviado con tres datos adjuntos de archivo.

  3. Debería ver el nuevo mensaje representado junto con los datos adjuntos de archivo.

    Captura de pantalla que muestra una aplicación de ejemplo con un mensaje entrante recibido con tres datos adjuntos de archivo.

Controlar datos adjuntos de imagen

Los datos adjuntos de imagen deben tratarse de forma diferente a los datos adjuntos estándar file. Los datos adjuntos de imagen tienen la propiedad attachmentType de image, lo que requiere el token de comunicación para recuperar las imágenes de tamaño completo o de vista previa.

Antes de continuar, complete el tutorial que muestra cómo habilitar la compatibilidad con imágenes insertadas en la aplicación Chat. En este tutorial, se describe cómo capturar imágenes que requieran un token de comunicación en el encabezado de solicitud. Después de recibir el blob de imágenes, es necesario crear una propiedad ObjectUrl que apunte a este blob. A continuación, se inserta esta URL en el atributo src de cada imagen insertada.

Como ya está familiarizado con el funcionamiento de las imágenes insertadas, puede representar datos adjuntos de imagen como imágenes insertadas normales.

En primer lugar, inserte una etiqueta image en el contenido del mensaje siempre que haya datos adjuntos de imagen:

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

Ahora tomaremos prestado fetchPreviewImages() del Tutorial: habilitación de la compatibilidad con imágenes insertadas y lo usaremos tal y como está, sin cambios:

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

Esta función necesita una propiedad tokenString, por lo que necesitará una copia global inicializada en init(), tal y como se muestra en el siguiente fragmento de código:

var tokenString = '';

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

Ya tiene compatibilidad con datos adjuntos de imágenes. Continúe ejecutando el código y véalo en acción.

Demostración de datos adjuntos de imagen

  1. Envíe algunos datos adjuntos de imagen desde el cliente de Teams.

    Captura de pantalla del cliente de Teams mostrando un cuadro de envío con una imagen adjunta cargada.

  2. Al enviar los datos adjuntos de imagen, observe que se convierte en una imagen insertada en el lado del cliente de Teams.

    Captura de pantalla del cliente de Teams mostrando un mensaje con la imagen adjunta enviada al otro participante.

  3. Vuelva a la aplicación de ejemplo y asegúrese de que se represente la misma imagen.

    Captura de pantalla de la aplicación de ejemplo que muestra un mensaje entrante con una imagen insertada renderizada.

En este tutorial aprenderá a habilitar la compatibilidad con imágenes insertadas mediante el SDK de Azure Communication Services Chat para C#.

En este tutorial, aprenderá a:

  • Controlar datos adjuntos de archivo.
  • Controlar datos adjuntos de imagen.

Requisitos previos

Código de ejemplo

Encuentre el código finalizado para este tutorial en GitHub.

Controlar archivos adjuntos

El SDK de Chat para C# devuelve una propiedad ChatAttachmentType de file para datos adjuntos de archivos normales y image para imágenes insertadas.

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


Por ejemplo, el siguiente JSON muestra cómo ChatAttachment podría mostrar los datos adjuntos de imagen y los datos adjuntos de un archivo al recibir solicitudes del lado servidor:

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

Ahora volvamos al controlador de eventos que creó en el inicio rápido anterior y agreguemos cierta lógica adicional para controlar los datos adjuntos con la propiedad ChatAttachmentType de 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}");
    }
}

En concreto, para cada archivo adjunto, obtendrá la propiedad previewUrl y creará una lista de direcciones URL en el for loop. A continuación, insertará la cadena junto con el contenido del mensaje de Chat.

Controlar datos adjuntos de imagen

Es necesario controlar los datos adjuntos de imagen de forma diferente a los datos adjuntos estándar file. Los datos adjuntos de imagen tienen la propiedad ChatAttachmentType de image, que requiere el token de comunicación para recuperar las imágenes de tamaño completo o de vista previa.

Antes de continuar, finalice el tutorial Habilitación de la compatibilidad con imágenes insertadas. Para identificar los datos adjuntos de imágenes, es necesario averiguar si el contenido del mensaje contiene el mismo id. de imagen de los datos adjuntos.

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

Si esta marca es true, aplique la lógica de imagen insertada para representarla:

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

La aplicación ya admite adjuntar imágenes.

Pasos siguientes