New to Telerik UI for ASP.NET MVCStart a free 30-day trial

Upload and Download Files in the Chat

Updated on Mar 26, 2026

Environment

ProductTelerik 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:

  1. Configure the Chat with file attachment restrictions and custom file actions.
  2. Handle the SendMessage event to upload files via AJAX to a server endpoint.
  3. Handle the Download event to save files using kendo.saveAs.
  4. Handle the FileMenuAction event 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.

Razor
@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.

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

C#
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.

See Also