New to Kendo UI for jQuery? Start a free 30-day trial
Upload and Download Files in the Chat
Updated on Jun 17, 2026
Environment
| Product | Progress® 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:
- Enable file attachments with allowed extension restrictions using the
fileAttachmentoption. - Define custom file actions (Download and View Details) through the
fileActionsconfiguration. - Upload files to a server endpoint via AJAX inside the
sendMessageevent handler. - Handle file downloads in the
downloadevent usingkendo.saveAs. - Show file metadata and image previews inside a Kendo UI Window when the custom "View Details" action is triggered through the
fileMenuActionevent.
Configure the Chat with File Attachments and Actions
The following example initializes the Chat with the fileAttachment and fileActions options, wires up the sendMessage, fileMenuAction, and download events, and opens a Kendo UI Window to display file metadata when a user selects View Details.
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.