Upload and Download Files in the Chat
Environment
| Product | Telerik UI for ASP.NET MVC Chat |
Description
How can I allow users to attach files to Chat messages, upload them to the server, download them, and view file details through a popup window in the Telerik UI for UI for ASP.NET MVC Chat?
Solution
To integrate file upload and download capabilities into the Chat component:
- Configure the Chat with file attachment restrictions and custom file actions.
- Handle the
SendMessageevent to upload files via AJAX to a server endpoint. - Handle the
Downloadevent to save files usingkendo.saveAs. - Handle the
FileMenuActionevent to display file details inside a Kendo UI Window.
View Configuration
The Chat is configured using the HtmlHelper with FileAttachment restrictions, custom FileActions, and event handlers for SendMessage, Download, and FileMenuAction.
@model List<ChatMessage>
<div class="k-d-flex k-justify-content-center">
@(Html.Kendo().Chat()
.Name("chat")
.Height("600px")
.Width("400px")
.FileAttachment(f => f.Restrictions(r => r.AllowedExtensions(".jpg", ".png", ".pdf", ".xlsx", ".docx", ".txt")))
.FileActions(actions =>
{
actions.Add().Name("download").Text("Download").Icon("download");
actions.Add().Name("viewdetails").Text("View Details").Icon("eye");
})
.AuthorId("1")
.BindTo(Model)
.Events(ev =>
{
ev.SendMessage("onSendMessage");
ev.Download("onDownload");
ev.FileMenuAction("onFileMenuAction");
})
)
</div>
@(Html.Kendo().Window()
.Name("fileDetailsWindow")
.Title("File Details")
.Visible(false)
.Modal(true)
.Width(420)
.Actions(actions => actions.Close())
)Client-Side Event Handlers
The onSendMessage handler uploads attached files to the server and posts a response message back into the Chat. The onDownload handler saves files via kendo.saveAs. The onFileMenuAction handler displays file metadata in a Kendo UI Window.
function uploadFilesToServer(files) {
return new Promise(function (resolve, reject) {
var formData = new FormData();
files.forEach(function (file) {
formData.append("files", file.rawFile);
});
$.ajax({
url: "@Url.Action("SaveFiles", "Chat")",
type: "POST",
data: formData,
processData: false,
contentType: false,
success: function (response) {
if (response.success) {
resolve(response.files);
} else {
reject(new Error(response.message || "Upload failed."));
}
},
error: function (xhr, status, error) {
reject(new Error("Upload failed: " + error));
}
});
});
}
function onSendMessage(e) {
var chat = e.sender;
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 };
});
chat.postMessage({
authorId: 2,
authorName: "Peter Smith",
text: "Thanks for uploading " + files.length + " file(s)!",
files: peerFiles,
timestamp: new Date()
});
});
}
}
function onDownload(e) {
if (!e.files || e.files.length === 0) return;
e.files.forEach(function (file) {
if (file.url) {
fetch(file.url)
.then(function (response) { return response.blob(); })
.then(function (blob) {
kendo.saveAs({ dataURI: blob, fileName: file.name });
});
} else if (file.rawFile) {
kendo.saveAs({ dataURI: file.rawFile, fileName: file.name });
}
});
}
function onFileMenuAction(e) {
if (e.type === "viewdetails") {
showFileDetails(e.file);
}
}
function showFileDetails(file) {
var extension = file.extension || "";
var displayable = [".bmp", ".gif", ".jpeg", ".jpg", ".png", ".svg", ".svgz"];
var canPreview = displayable.indexOf(extension.toLowerCase()) > -1 && file.url;
var contentHtml = "";
if (canPreview) {
contentHtml += '<div style="text-align:center;margin-bottom:16px;"><img src="' +
kendo.htmlEncode(file.url) + '" style="max-width:100%;max-height:200px;border-radius:4px;" /></div>';
}
contentHtml += '<table class="k-table" style="width:100%;border-collapse:collapse;">' +
'<tr><td style="padding:8px;font-weight:bold;">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;">' + (file.size || 0) + ' bytes</td></tr>' +
'<tr><td style="padding:8px;font-weight:bold;">Type</td><td style="padding:8px;">' + kendo.htmlEncode(extension || "Unknown") + '</td></tr>' +
'</table>';
var wnd = $("#fileDetailsWindow").data("kendoWindow");
wnd.content(contentHtml);
wnd.title("File Details - " + (file.name || "Unknown"));
wnd.center().open();
}
Controller
The controller action provides initial Chat messages and a SaveFiles endpoint that stores uploaded files in session and returns download URLs.
public partial class ChatController : Controller
{
[Demo]
public IActionResult Access_Files()
{
var data = new List<ChatMessage>()
{
new ChatMessage {
Id = "1",
AuthorId = "2",
AuthorName = "Peter Smith",
Text = "Use the paperclip button to attach files.",
TimeStamp = DateTime.Now
}
};
return View(data);
}
[HttpPost]
public IActionResult SaveFiles(List<IFormFile> files)
{
if (files == null || files.Count == 0)
return Json(new { success = false, message = "No files uploaded." });
var results = new List<object>();
foreach (var file in files)
{
if (file.Length == 0) continue;
var fileName = Path.GetFileName(file.FileName);
var fileId = Guid.NewGuid().ToString("N");
byte[] fileBytes;
using (var ms = new MemoryStream())
{
file.CopyTo(ms);
fileBytes = ms.ToArray();
}
HttpContext.Session.Set(fileId, fileBytes);
results.Add(new
{
url = Url.Action("DownloadFile", "Chat", new { fileId }, Request.Scheme),
name = fileName,
size = file.Length,
extension = Path.GetExtension(fileName)
});
}
return Json(new { success = true, files = results });
}
public IActionResult DownloadFile(string fileId)
{
var fileData = HttpContext.Session.Get(fileId);
if (fileData == null) return NotFound();
return File(fileData, "application/octet-stream");
}
}
For a runnable example, refer to the Chat Access Files demo.