Skip to main content
RapidDev - Software Development Agency
outsystems-tutorial

File Upload and Download Patterns in OutSystems

Use the Upload widget to capture a file into FileContent (Binary Data) and FileName (Text) local variables, then store them in an entity attribute of type Binary Data via a Server Action. To download, call the built-in Download action in a Server Action, passing the Binary Data and MIME type. Both patterns work identically in O11 and ODC.

What you'll learn

  • How to add an Upload widget and bind it to Binary Data and FileName variables
  • Storing uploaded file content in a database entity using a Server Action
  • Validating file type and size before the upload is saved
  • Triggering a file download from a Button using the built-in Download action
  • Serving files stored in the database with the correct MIME type
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate8 min read30-40 minOutSystems 11 and ODCMarch 2026RapidDev Engineering Team
TL;DR

Use the Upload widget to capture a file into FileContent (Binary Data) and FileName (Text) local variables, then store them in an entity attribute of type Binary Data via a Server Action. To download, call the built-in Download action in a Server Action, passing the Binary Data and MIME type. Both patterns work identically in O11 and ODC.

File Upload and Download in OutSystems

Many business applications need document management — contracts, invoices, profile images. OutSystems handles files as Binary Data, the same data type used for Excel exports and PDF generation. This tutorial walks through a complete document upload/download cycle: adding the Upload widget, capturing the file into variables, validating type and size, persisting to the database, and offering it back as a download.

Prerequisites

  • A Reactive Web App with at least one screen open in Service Studio
  • A Document entity in the Data tab with attributes: Id (Long Integer), Name (Text), Content (Binary Data), MimeType (Text), UploadedAt (DateTime)
  • Basic knowledge of Server Actions and how to call them from Client Actions

Step-by-step guide

1

Add the Upload widget and bind file variables

Open your screen in the Screen Editor. In the Toolbox, search for 'Upload' and drag it onto the canvas inside a Form widget. In the Properties Panel: - FileContent: create a new Local Variable 'FileContent' of type Binary Data - FileName: create a new Local Variable 'FileName' of type Text - Accept: set to '.pdf,.doc,.docx' to restrict file types at the browser level (this is advisory only — always validate server-side too) - MaxFileSize: set to 10485760 (10 MB, expressed in bytes) After the user picks a file, OutSystems automatically populates FileContent and FileName from the browser file picker.

Expected result: An Upload widget is visible on the screen. Opening the app and clicking the widget opens the OS file picker filtered to PDF and Word documents.

2

Create the Document entity in the Data tab

If you have not already created the entity: Data tab → Entities → Database → right-click → Add Entity. Name it 'Document'. Right-click the entity → Add Entity Attribute for each field: - Name: Text, Is Mandatory: Yes - Content: Binary Data, Is Mandatory: Yes - MimeType: Text, Is Mandatory: Yes, Length: 100 - UploadedAt: DateTime, Is Mandatory: Yes The Id attribute (Long Integer, auto-increment) is created automatically. TrueChange will show no errors after this step.

Expected result: Document entity appears in the Entity Diagram with five attributes. OutSystems auto-creates CreateDocument, UpdateDocument, DeleteDocument, GetDocument Server Actions.

3

Create a Server Action to save the uploaded file

Logic tab → Server Actions → right-click → Add Server Action. Name it 'SaveDocument'. Add input parameters: - FileContent: Binary Data - FileName: Text - MimeType: Text Action flow: Start → CreateDocument → Assign DocumentId = CreateDocument.Id → End For the CreateDocument call (drag from Toolbox or Logic tab), set the entity record attributes: - Document.Name = FileName - Document.Content = FileContent - Document.MimeType = MimeType - Document.UploadedAt = CurrDateTime() Add an Output Parameter 'DocumentId' (Long Integer) to return the new record Id.

typescript
1/* CreateDocument node attribute mapping */
2Document.Name = FileName
3Document.Content = FileContent
4Document.MimeType = MimeType
5Document.UploadedAt = CurrDateTime()

Expected result: SaveDocument Server Action compiles with no TrueChange errors. It accepts a binary file and creates a Document record.

4

Validate file type and size in the submit Client Action

Add an Upload button to the screen. Right-click → Add OnClick Client Action. In the Action Flow Editor: Start → If: FileContent = NullBinaryData() → [True] show Message 'Please select a file' → End → If: not (Index(ToLower(FileName), ".pdf", 0, False, True) >= 0 or Index(ToLower(FileName), ".doc", 0, False, True) >= 0 or Index(ToLower(FileName), ".docx", 0, False, True) >= 0) → [True] set UploadWidget.Valid = False, UploadWidget.ValidationMessage = 'Only PDF and Word files are accepted' → End → Assign: MimeType = If(Index(ToLower(FileName), ".pdf", 0, False, True) >= 0, "application/pdf", "application/vnd.openxmlformats-officedocument.wordprocessingml.document") → SaveDocument (Server Action) with FileContent, FileName, MimeType → Message: 'File uploaded successfully' (Success) → End

typescript
1/* File type check expression */
2not (
3 Index(ToLower(FileName), ".pdf", 0, False, True) >= 0
4 or Index(ToLower(FileName), ".doc", 0, False, True) >= 0
5 or Index(ToLower(FileName), ".docx", 0, False, True) >= 0
6)
7
8/* MIME type assignment */
9MimeType = If(
10 Index(ToLower(FileName), ".pdf", 0, False, True) >= 0,
11 "application/pdf",
12 "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
13)

Expected result: Uploading a disallowed file type shows the validation error inline. Uploading a valid PDF or Word document creates a Document record in the database.

5

Build the Download Server Action

Logic tab → Server Actions → right-click → Add Server Action. Name it 'DownloadDocument'. Add input parameter: DocumentId (Long Integer). Action flow: Start → GetDocument (use the auto-created GetDocument entity action) with Id = DocumentId → If: GetDocument.Document.Id = NullIdentifier() → [True] raise User Exception 'Document not found' → Download node (drag from Toolbox → System category) FileContent: GetDocument.Document.Content FileName: GetDocument.Document.Name MimeType: GetDocument.Document.MimeType → End The Download node triggers the browser's native download dialog. It does not return — execution ends after the browser receives the file.

typescript
1/* Download node properties */
2FileContent: GetDocument.Document.Content
3FileName: GetDocument.Document.Name
4MimeType: GetDocument.Document.MimeType

Expected result: Calling DownloadDocument with a valid DocumentId triggers a browser file download with the stored filename and correct MIME type.

6

Add a Download button to the document list

On a screen showing a list of documents (Table or List widget bound to a GetDocuments Aggregate), add a Button or Link widget in each row. Set its OnClick to a new Client Action. In that Client Action: Start → DownloadDocument (call Server Action) with DocumentId = GetDocuments.List.Current.Document.Id → End Note: Download Server Actions must be called from Client Actions. You cannot call a Download action directly from a Server Action that is part of a navigation flow — it must be triggered by a user interaction.

Expected result: Clicking the Download button on a list row triggers the browser download dialog with the correct filename.

Complete working example

FileUploadDownload_actions.txt
1/* ============================================================
2 ENTITY: Document
3 Attributes:
4 Id : Long Integer (auto PK)
5 Name : Text (mandatory)
6 Content : Binary Data (mandatory)
7 MimeType : Text (mandatory, length 100)
8 UploadedAt : DateTime (mandatory)
9 ============================================================ */
10
11/* ============================================================
12 SERVER ACTION: SaveDocument
13 Input: FileContent (Binary Data), FileName (Text), MimeType (Text)
14 Output: DocumentId (Long Integer)
15 ============================================================ */
16Start
17 --> CreateDocument:
18 Document.Name = FileName
19 Document.Content = FileContent
20 Document.MimeType = MimeType
21 Document.UploadedAt = CurrDateTime()
22 --> Assign: DocumentId = CreateDocument.Id
23 --> End
24
25/* ============================================================
26 SERVER ACTION: DownloadDocument
27 Input: DocumentId (Long Integer)
28 ============================================================ */
29Start
30 --> GetDocument: Id = DocumentId
31 --> If: GetDocument.Document.Id = NullIdentifier()
32 [True] --> Raise UserException: "Document not found"
33 [False]--> Download:
34 FileContent = GetDocument.Document.Content
35 FileName = GetDocument.Document.Name
36 MimeType = GetDocument.Document.MimeType
37 --> End
38
39/* ============================================================
40 CLIENT ACTION: ButtonUploadOnClick
41 ============================================================ */
42Start
43 --> If: FileContent = NullBinaryData()
44 [True] --> Message: "Please select a file" (Warning) --> End
45 --> If: not valid extension
46 [True] --> Assign: UploadWidget.Valid = False
47 UploadWidget.ValidationMessage = "Only PDF and Word files accepted"
48 --> End
49 --> Assign: MimeType = (extension-based expression)
50 --> SaveDocument: FileContent, FileName, MimeType
51 --> Message: "File uploaded successfully" (Success)
52 --> End

Common mistakes

Why it's a problem: Calling the Download action from a Server Action inside a navigation flow

How to avoid: The Download action must be called from a Client Action triggered by a user interaction (button click). Calling it from a background Server Action will not reach the browser. Structure: Button OnClick → Client Action → calls DownloadDocument Server Action.

Why it's a problem: Storing only the FileName without the MimeType in the entity

How to avoid: Without MimeType, you cannot serve the correct Content-Type header, so the browser may not know how to handle the file. Always store MimeType in a separate entity attribute and pass it to the Download action.

Why it's a problem: Checking file type using FileName = '.pdf' instead of Index()

How to avoid: Simple equality checks on FileName will only match files literally named '.pdf'. Use Index(ToLower(FileName), '.pdf', 0, False, True) >= 0 to check if the extension appears anywhere in the filename.

Why it's a problem: Forgetting to set the Upload widget's FileContent and FileName Variable properties

How to avoid: If Variable is not set, the picked file is not bound to any local variable and FileContent remains NullBinaryData(). Always bind both FileContent and FileName in the Upload widget Properties Panel.

Best practices

  • Always validate file type server-side in addition to the browser-level Accept property — the Accept filter is bypassed by drag-and-drop in many browsers.
  • Store MIME type alongside the binary content so downloads are served with the correct Content-Type header.
  • For large files (over 5 MB), consider chunked upload via a REST API instead of the standard Upload widget, which holds the entire file in memory.
  • Set a maximum upload size both in the Upload widget's MaxFileSize property and in Service Center → Administration → Server Configuration → Max Request Content Length.
  • Use NullBinaryData() to check for an empty upload rather than checking the length of the binary variable.
  • Consider adding a file size display after upload by reading the Length(FileContent) expression to give users feedback before they submit.

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I'm building a document management feature in OutSystems 11 Reactive Web. Explain how to: (1) use the Upload widget to capture a file into Binary Data and FileName variables, (2) validate the file extension is .pdf or .docx in a Client Action using OutSystems expression syntax, (3) store the file in a Document entity with Binary Data attribute in a Server Action, and (4) trigger a browser download from a list row button using the built-in Download action.

OutSystems Prompt

Add file upload and download to my OutSystems screen. Create: (1) Document entity with Id, Name (Text), Content (Binary Data), MimeType (Text, length 100), UploadedAt (DateTime) attributes. (2) SaveDocument Server Action that creates a Document record. (3) DownloadDocument Server Action using the Download system action. (4) Upload widget bound to FileContent and FileName variables with .pdf and .docx Accept filter. (5) ButtonUploadOnClick Client Action with NullBinaryData() check and extension validation before calling SaveDocument.

Frequently asked questions

Is there a file size limit for the Upload widget in OutSystems?

By default OutSystems Platform Server allows requests up to 28.6 MB. You can increase this in Service Center → Administration → Server Configuration → Max Request Content Length. The Upload widget's MaxFileSize property enforces a client-side limit in bytes before the file is sent to the server. Set both to be consistent.

How do I display an uploaded image on screen instead of downloading it?

Bind an Image widget's URL property to a server-provided URL that streams the binary content, or use the Image widget's Content property to bind a Binary Data expression directly (supported for screen-resident data). For images stored in the database, the easiest approach is to set Image.Content = Document.Content after fetching the record.

Can I upload multiple files at once with the Upload widget?

The standard Upload widget supports single file selection only. For multi-file upload, use the OutSystems UI FileUpload pattern from Forge, or implement a loop where the user selects and uploads files one at a time. The FilePicker Forge component provides drag-and-drop multi-file support.

Does file upload work the same way in ODC?

Yes, the Upload widget, Binary Data type, and Download action are identical in ODC. The only difference is where you configure the maximum request size — in ODC this is managed via ODC Portal settings rather than Service Center.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.