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
Add the Upload widget and bind file variables
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.
Create the Document entity in the Data tab
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.
Create a Server Action to save the uploaded file
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.
1/* CreateDocument node attribute mapping */2Document.Name = FileName3Document.Content = FileContent4Document.MimeType = MimeType5Document.UploadedAt = CurrDateTime()Expected result: SaveDocument Server Action compiles with no TrueChange errors. It accepts a binary file and creates a Document record.
Validate file type and size in the submit Client Action
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
1/* File type check expression */2not (3 Index(ToLower(FileName), ".pdf", 0, False, True) >= 04 or Index(ToLower(FileName), ".doc", 0, False, True) >= 05 or Index(ToLower(FileName), ".docx", 0, False, True) >= 06)78/* 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.
Build the Download Server Action
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.
1/* Download node properties */2FileContent: GetDocument.Document.Content3FileName: GetDocument.Document.Name4MimeType: GetDocument.Document.MimeTypeExpected result: Calling DownloadDocument with a valid DocumentId triggers a browser file download with the stored filename and correct MIME type.
Add a Download button to the document list
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
1/* ============================================================2 ENTITY: Document3 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 ============================================================ */1011/* ============================================================12 SERVER ACTION: SaveDocument13 Input: FileContent (Binary Data), FileName (Text), MimeType (Text)14 Output: DocumentId (Long Integer)15 ============================================================ */16Start17 --> CreateDocument:18 Document.Name = FileName19 Document.Content = FileContent20 Document.MimeType = MimeType21 Document.UploadedAt = CurrDateTime()22 --> Assign: DocumentId = CreateDocument.Id23 --> End2425/* ============================================================26 SERVER ACTION: DownloadDocument27 Input: DocumentId (Long Integer)28 ============================================================ */29Start30 --> GetDocument: Id = DocumentId31 --> If: GetDocument.Document.Id = NullIdentifier()32 [True] --> Raise UserException: "Document not found"33 [False]--> Download:34 FileContent = GetDocument.Document.Content35 FileName = GetDocument.Document.Name36 MimeType = GetDocument.Document.MimeType37 --> End3839/* ============================================================40 CLIENT ACTION: ButtonUploadOnClick41 ============================================================ */42Start43 --> If: FileContent = NullBinaryData()44 [True] --> Message: "Please select a file" (Warning) --> End45 --> If: not valid extension46 [True] --> Assign: UploadWidget.Valid = False47 UploadWidget.ValidationMessage = "Only PDF and Word files accepted"48 --> End49 --> Assign: MimeType = (extension-based expression)50 --> SaveDocument: FileContent, FileName, MimeType51 --> Message: "File uploaded successfully" (Success)52 --> EndCommon 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.
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.
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.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation