File Download
The Download command tells the frontend to initiate a file download for the user. The actual file is streamed from your backend's /downloadfile endpoint.
When your handler calls Download(), the command is returned in the /runevent response. The SDK receives it and triggers the download in the browser.
Command structure
{
"command": "Download",
"args": {
"fileName": "report-2025.xlsx",
"filePath": "my-bucket:reports/2025/report.xlsx"
}
}
fileName— the filename shown to the user in the browser's download prompt.filePath— the file identifier (bucket:path) that your/downloadfileendpoint uses to locate and stream the file.
C# implementation
public async Task Form_onClick(string widgetName)
{
if (widgetName == "exportbtn")
{
// Generate the export file
string fileId = await GenerateExcelReport(record.GetData());
// Trigger download in the browser
Download(fileId, "employee-export.xlsx");
}
}
Generating and downloading a file
private async Task<string> GenerateExcelReport(Dictionary<string, object> formData)
{
var employees = await _db.Employees
.Where(e => e.DepartmentId.ToString() == formData.GetString("departmentId"))
.ToListAsync();
byte[] excelBytes = await _reportService.GenerateExcel(employees);
string path = $"exports/{Guid.NewGuid()}/employees.xlsx";
string fileId = await _fileStorage.UploadFileAsync(
path, excelBytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
return fileId;
}
Downloading an existing attachment
public async Task Form_onClick(string widgetName)
{
if (widgetName == "downloadattachmentbtn")
{
DataRecord record = (DataRecord)(await GetRecord())!;
await record.LoadRecord();
string? fileId = record.GetField("document")?.ToString();
string? fileName = record.GetField("documentName")?.ToString() ?? "document.pdf";
if (string.IsNullOrEmpty(fileId))
{
ShowMessage("warning", "No document attached.");
return;
}
Download(fileId, fileName);
}
}
How /downloadfile works
When the Download command is received, the SDK calls your /downloadfile endpoint:
[HttpPost("downloadfile")]
public async Task<IActionResult> DownloadFile([FromBody] FileRequestDto request)
{
string[] parts = request.FileId!.Split(':', 2);
string bucketName = parts[0];
string filePath = parts[1];
string fileName = Path.GetFileName(filePath);
Stream? fileStream = await _fileStorage.GetFileStreamAsync(filePath, bucketName);
if (fileStream == null)
return NotFound("File not found");
Response.ContentType = _fileStorage.GetMimeType(fileName);
Response.Headers.Append("Content-Disposition",
$"attachment; filename*=UTF-8''{Uri.EscapeDataString(fileName)}");
await _fileStorage.WriteInChunksAsync(Response.Body, fileStream);
return new EmptyResult();
}
The Content-Disposition: attachment header triggers the browser's save-file dialog.
Inline vs. attachment
If you want the browser to attempt to display the file (e.g. open a PDF in a new tab) instead of saving it, use the document previewer command instead. See Triggering a Document Previewer.