New to Kendo UI for jQueryStart a free 30-day trial

Upload and Download Files in the Chat

Updated on Mar 26, 2026

Environment

ProductProgress� Kendo UI� Chat for jQuery

Description

How can I allow users to attach files to Chat messages, download them, and view file details through a popup window in the Kendo UI for jQuery Chat?

Solution

The following approach demonstrates how to:

  1. Enable file attachments with allowed extension restrictions using the fileAttachment option.
  2. Define custom file actions (Download and View Details) through the fileActions configuration.
  3. Upload files to a server endpoint via AJAX inside the sendMessage event handler.
  4. Handle file downloads in the download event using kendo.saveAs.
  5. Show file metadata and image previews inside a Kendo UI Window when the custom "View Details" action is triggered through the fileMenuAction event.

Configure the Chat with File Attachments and Actions

HTML
<div class="k-d-flex k-justify-content-center">
    <div id="chat"></div>
</div>

<div id="fileDetailsWindow"></div>

<script>
    var currentUser = {
        id: 1,
        name: "John Doe",
        iconUrl: "https://demos.telerik.com/kendo-ui/content/web/Customers/RICSU.jpg",
        iconAltText: "John's profile picture"
    };

    var imageExtensions = [".ai", ".dds", ".heic", ".jpe", ".jfif", ".jif", ".jp2", ".jps",
        ".eps", ".bmp", ".gif", ".jpeg", ".jpg", ".png", ".ps", ".psd", ".svg", ".svgz", ".tif", ".tiff"];

    function isDisplayableImage(extension) {
        if (!extension) return false;
        var ext = extension.toLowerCase();
        if (ext.charAt(0) !== ".") ext = "." + ext;
        var displayable = [".bmp", ".gif", ".jpeg", ".jpg", ".png", ".svg", ".svgz"];
        return displayable.indexOf(ext) > -1;
    }

    function formatFileSize(bytes) {
        if (bytes === 0) return "0 Bytes";
        var sizes = ["Bytes", "KB", "MB", "GB"];
        var i = Math.floor(Math.log(bytes) / Math.log(1024));
        return parseFloat((bytes / Math.pow(1024, i)).toFixed(2)) + " " + sizes[i];
    }

    function uploadFilesToServer(files) {
        return new Promise(function (resolve, reject) {
            var formData = new FormData();
            files.forEach(function (file) {
                formData.append("files", file.rawFile);
            });

            $.ajax({
                url: "../chat/savefiles",
                type: "POST",
                data: formData,
                processData: false,
                contentType: false,
                success: function (response) {
                    if (response.success) {
                        resolve(response.files);
                    } else {
                        reject(response.message || "Upload failed");
                    }
                },
                error: function (xhr, status, error) {
                    reject(error);
                }
            });
        });
    }

    $(document).ready(function () {
        $("#fileDetailsWindow").kendoWindow({
            title: "File Details",
            visible: false,
            modal: true,
            width: 420,
            actions: ["Close"]
        });

        $("#chat").kendoChat({
            height: "600px",
            width: "400px",
            authorId: "1",
            fileAttachment: {
                restrictions: {
                    allowedExtensions: [".jpg", ".png", ".pdf", ".xlsx", ".docx", ".txt"]
                }
            },
            fileActions: [
                { name: "download", text: "Download", icon: "download" },
                { name: "viewdetails", text: "View Details", icon: "eye" }
            ],
            dataSource: {
                data: [
                    {
                        id: "1",
                        authorId: "2",
                        authorName: "Peter Smith",
                        authorImageUrl: "https://demos.telerik.com/kendo-ui/content/web/Customers/GOURL.jpg",
                        authorImageAltText: "Peter's profile picture",
                        text: "Use the paperclip button in the message input area to attach files, and the file action menu to download or delete them.",
                        timestamp: new Date()
                    },
                    {
                        id: "2",
                        authorId: "2",
                        authorName: "Peter Smith",
                        authorImageUrl: "https://demos.telerik.com/kendo-ui/content/web/Customers/GOURL.jpg",
                        authorImageAltText: "Peter's profile picture",
                        text: "Go ahead and upload your first file. Once uploaded, I will receive it with a download link so both of us can access it.",
                        timestamp: new Date()
                    }
                ]
            },
            sendMessage: function (e) {
                var chat = e.sender;

                e.message.authorId = currentUser.id;
                e.message.authorName = currentUser.name;
                e.message.authorImageUrl = currentUser.iconUrl;
                e.message.authorImageAltText = currentUser.iconAltText;

                if (e.message.files && e.message.files.length > 0) {
                    var files = e.message.files;

                    uploadFilesToServer(files).then(function (results) {
                        var peerFiles = results.map(function (result) {
                            return {
                                name: result.name,
                                size: result.size,
                                extension: result.extension,
                                url: result.url
                            };
                        });

                        var fileDetails = peerFiles.map(function (f) {
                            return "Name: " + f.name +
                                ", Size: " + formatFileSize(f.size) +
                                ", Type: " + f.extension;
                        }).join("\n\n");

                        chat.postMessage({
                            authorId: 2,
                            authorName: "Peter Smith",
                            authorImageUrl: "https://demos.telerik.com/kendo-ui/content/web/Customers/GOURL.jpg",
                            authorImageAltText: "Peter's profile picture",
                            text: "Thanks for uploading " + files.length + " file(s). I can now access and download them!\n\nFile details:\n" + fileDetails,
                            files: peerFiles,
                            timestamp: new Date()
                        });
                    }).catch(function (error) {
                        console.error("File upload failed:", error);
                        chat.postMessage({
                            authorId: 2,
                            authorName: "Peter Smith",
                            authorImageUrl: "https://demos.telerik.com/kendo-ui/content/web/Customers/GOURL.jpg",
                            authorImageAltText: "Peter's profile picture",
                            text: "Sorry, there was an error uploading the file(s). Please try again.",
                            timestamp: new Date()
                        });
                    });
                } else {
                    setTimeout(function () {
                        chat.postMessage({
                            authorId: 2,
                            authorName: "Peter Smith",
                            authorImageUrl: "https://demos.telerik.com/kendo-ui/content/web/Customers/GOURL.jpg",
                            authorImageAltText: "Peter's profile picture",
                            text: "Got it! You can also attach files using the file button in the message input area.",
                            timestamp: new Date()
                        });
                    }, 500);
                }
            },
            fileMenuAction: function (e) {
                if (e.type === "viewdetails") {
                    showFileDetails(e.file);
                }
            },
            download: function (e) {
                if (!e.files || e.files.length === 0) {
                    return;
                }

                e.files.forEach(function (file) {
                    if (file.url) {
                        // Download from server URL
                        fetch(file.url)
                            .then(function (response) {
                                if (!response.ok) {
                                    throw new Error("Download failed");
                                }
                                return response.blob();
                            })
                            .then(function (blob) {
                                kendo.saveAs({
                                    dataURI: blob,
                                    fileName: file.name
                                });
                            })
                            .catch(function (err) {
                                console.error("Download error:", err);
                                kendo.alert("File is not available for download: " + file.name);
                            });
                    } else if (file.rawFile) {
                        // Fallback for files still in browser memory
                        kendo.saveAs({
                            dataURI: file.rawFile,
                            fileName: file.name
                        });
                    } else {
                        kendo.alert("File is not available for download: " + file.name);
                    }
                });
            }
        });
    });

    function showFileDetails(file) {
        var fileUrl = file.url;
        var extension = file.extension || "";
        var canPreview = isDisplayableImage(extension) && fileUrl;
        var iconName = kendo.getFileGroup(extension, true);
        var contentHtml = "";

        if (canPreview) {
            contentHtml += '<div style="text-align: center; margin-bottom: 16px;">' +
                '<img src="' + kendo.htmlEncode(fileUrl) + '" alt="' + kendo.htmlEncode(file.name) + '" style="max-width: 100%; max-height: 200px; border-radius: 4px;" />' +
                '</div>';
        } else {
            contentHtml += '<div style="text-align: center; margin-bottom: 16px;">' +
                kendo.ui.icon({ icon: iconName, size: "xxxlarge" }) +
                '</div>';
        }

        contentHtml += '<table class="k-table" style="width: 100%; border-collapse: collapse;">' +
            '<tbody>' +
            '<tr><td style="padding: 8px; font-weight: bold; width: 100px;">Name</td><td style="padding: 8px;">' + kendo.htmlEncode(file.name || "N/A") + '</td></tr>' +
            '<tr><td style="padding: 8px; font-weight: bold;">Size</td><td style="padding: 8px;">' + formatFileSize(file.size || 0) + '</td></tr>' +
            '<tr><td style="padding: 8px; font-weight: bold;">Type</td><td style="padding: 8px;">' + kendo.htmlEncode(extension || "Unknown") + '</td></tr>' +
            '<tr><td style="padding: 8px; font-weight: bold;">Category</td><td style="padding: 8px;">' + kendo.htmlEncode(kendo.getFileGroup(extension, false) || "file") + '</td></tr>' +
            '</tbody></table>';

        var wnd = $("#fileDetailsWindow").data("kendoWindow");
        wnd.content(contentHtml);
        wnd.title("File Details - " + (file.name || "Unknown"));
        wnd.center().open();
    }
</script>

<style>
    .k-chat {
        min-width: 300px !important;
    }
</style>

For a runnable example, refer to the Chat Access Files demo.

See Also