NAME File Upload Examples - Practical examples for implementing file uploads SYNOPSIS Examples for implementing file upload endpoints and processing files in RSX applications using the File_Attachment_Model factory methods. DESCRIPTION This document provides practical, copy-paste ready examples for common file upload scenarios. All examples use the factory methods on File_Attachment_Model which handle storage, deduplication, and metadata automatically. BASIC HTTP FILE UPLOAD Simple file upload endpoint with validation: Controller: /rsx/app/frontend/files/files_controller.php validate([ 'file' => 'required|file|max:10240', // 10MB max ]); // Get site_id from authenticated user $site_id = RsxAuth::session()->site_id; // Create attachment $attachment = File_Attachment_Model::create_from_upload( $request->file('file'), [ 'site_id' => $site_id, 'fileable_category' => 'general' ] ); return [ 'success' => true, 'key' => $attachment->key, 'filename' => $attachment->file_name, 'size' => $attachment->file_storage->size, 'url' => $attachment->get_url() ]; } } USER AVATAR UPLOAD Upload and attach to user model with category: #[Route('/profile/avatar/upload', method: 'POST')] #[Auth('Permission::authenticated()')] #[Ajax_Endpoint] public static function upload_avatar(Request $request, array $params = []) { $request->validate([ 'avatar' => 'required|image|max:5120', // 5MB, images only ]); $user = RsxAuth::user(); $site_id = RsxAuth::session()->site_id; // Delete old avatar if exists $old_avatar = File_Attachment_Model::where('fileable_type', 'User_Model') ->where('fileable_id', $user->id) ->where('fileable_category', 'avatar') ->first(); if ($old_avatar) { $old_avatar->delete(); // Auto-cleanup handles storage } // Create new avatar $attachment = File_Attachment_Model::create_from_upload( $request->file('avatar'), [ 'site_id' => $site_id, 'fileable_type' => 'User_Model', 'fileable_id' => $user->id, 'fileable_category' => 'avatar', 'fileable_type_meta' => 'profile' ] ); return [ 'success' => true, 'url' => $attachment->get_url() ]; } MULTIPLE FILE UPLOAD Handle multiple files with ordering: #[Route('/project/{id}/documents/upload', method: 'POST')] #[Auth('Permission::authenticated()')] #[Ajax_Endpoint] public static function upload_documents(Request $request, array $params = []) { $project_id = $params['id']; $site_id = RsxAuth::session()->site_id; $request->validate([ 'documents' => 'required|array', 'documents.*' => 'file|max:20480', // 20MB per file ]); $attachments = []; $order = 1; foreach ($request->file('documents') as $file) { $attachment = File_Attachment_Model::create_from_upload( $file, [ 'site_id' => $site_id, 'fileable_type' => 'Project_Model', 'fileable_id' => $project_id, 'fileable_category' => 'document', 'fileable_order' => $order++ ] ); $attachments[] = [ 'key' => $attachment->key, 'filename' => $attachment->file_name, 'url' => $attachment->get_url() ]; } return [ 'success' => true, 'files' => $attachments ]; } GENERATED FILE EXPORT Generate CSV and attach to report: #[Route('/reports/{id}/export', method: 'POST')] #[Auth('Permission::authenticated()')] #[Ajax_Endpoint] public static function export_report(Request $request, array $params = []) { $report_id = $params['id']; $site_id = RsxAuth::session()->site_id; // Generate CSV content $data = Report_Model::find($report_id)->get_data(); $csv = "Name,Value,Date\n"; foreach ($data as $row) { $csv .= "{$row->name},{$row->value},{$row->date}\n"; } // Create attachment from string $attachment = File_Attachment_Model::create_from_string( $csv, "report-{$report_id}-" . date('Y-m-d') . ".csv", [ 'site_id' => $site_id, 'fileable_type' => 'Report_Model', 'fileable_id' => $report_id, 'fileable_category' => 'export', 'fileable_meta' => [ 'generated_at' => now()->toIso8601String(), 'generated_by' => RsxAuth::id() ] ] ); return [ 'success' => true, 'download_url' => $attachment->get_download_url() ]; } IMPORT FROM URL Download and import external file: #[Route('/resources/import', method: 'POST')] #[Auth('Permission::admin()')] #[Ajax_Endpoint] public static function import_from_url(Request $request, array $params = []) { $site_id = RsxAuth::session()->site_id; $request->validate([ 'url' => 'required|url', ]); try { $attachment = File_Attachment_Model::create_from_url( $request->input('url'), [ 'site_id' => $site_id, 'fileable_category' => 'import', 'fileable_meta' => [ 'source_url' => $request->input('url'), 'imported_at' => now()->toIso8601String(), 'imported_by' => RsxAuth::id() ] ] ); return [ 'success' => true, 'key' => $attachment->key, 'filename' => $attachment->file_name ]; } catch (\Exception $e) { return [ 'success' => false, 'error' => $e->getMessage() ]; } } BULK DISK IMPORT Import files from directory: #[Route('/admin/import/bulk', method: 'POST')] #[Auth('Permission::admin()')] #[Ajax_Endpoint] public static function bulk_import(Request $request, array $params = []) { $site_id = RsxAuth::session()->site_id; $request->validate([ 'directory' => 'required|string', ]); $directory = $request->input('directory'); if (!is_dir($directory)) { return [ 'success' => false, 'error' => 'Directory not found' ]; } $files = glob($directory . '/*'); $imported = []; $errors = []; foreach ($files as $file_path) { if (!is_file($file_path)) { continue; } try { $attachment = File_Attachment_Model::create_from_disk( $file_path, [ 'site_id' => $site_id, 'fileable_category' => 'bulk_import', 'fileable_meta' => [ 'original_path' => $file_path, 'imported_at' => now()->toIso8601String() ] ] ); $imported[] = [ 'filename' => $attachment->file_name, 'key' => $attachment->key ]; } catch (\Exception $e) { $errors[] = [ 'file' => basename($file_path), 'error' => $e->getMessage() ]; } } return [ 'success' => true, 'imported' => count($imported), 'errors' => count($errors), 'files' => $imported, 'failed' => $errors ]; } FILE VALIDATION EXAMPLES Common validation rules: // Images only, max 5MB 'file' => 'required|image|max:5120' // PDFs only, max 10MB 'file' => 'required|mimes:pdf|max:10240' // Documents (PDF, Word, Excel), max 20MB 'file' => 'required|mimes:pdf,doc,docx,xls,xlsx|max:20480' // Videos, max 100MB 'file' => 'required|mimes:mp4,mov,avi|max:102400' // Archives, max 50MB 'file' => 'required|mimes:zip,tar,gz|max:51200' // Multiple files, each max 10MB 'files' => 'required|array', 'files.*' => 'file|max:10240' ERROR HANDLING All factory methods throw exceptions on failure: try { $attachment = File_Attachment_Model::create_from_upload( $request->file('file'), ['site_id' => $site_id] ); return ['success' => true, 'key' => $attachment->key]; } catch (\Exception $e) { return [ 'success' => false, 'error' => $e->getMessage() ]; } Common exceptions: - File not found (create_from_disk) - Network timeout (create_from_url) - Missing site_id parameter - Disk write failure - Invalid file type RETRIEVING UPLOADED FILES Get files attached to a model: // Get all files for a project $files = File_Attachment_Model::where('fileable_type', 'Project_Model') ->where('fileable_id', $project_id) ->get(); // Get specific category $documents = File_Attachment_Model::where('fileable_type', 'Project_Model') ->where('fileable_id', $project_id) ->where('fileable_category', 'document') ->orderBy('fileable_order') ->get(); // Get by type meta $avatar = File_Attachment_Model::where('fileable_type', 'User_Model') ->where('fileable_id', $user_id) ->where('fileable_type_meta', 'profile') ->first(); DELETING FILES Simple deletion (auto-cleanup handles storage): $attachment = File_Attachment_Model::find_by_key($key); $attachment->delete(); // Storage automatically deleted if no other attachments reference it Delete all files for a model: File_Attachment_Model::where('fileable_type', 'Project_Model') ->where('fileable_id', $project_id) ->delete(); DRAG-AND-DROP UPLOADS WITH DROPPABLE Use Droppable to enable drag-and-drop file uploads in JQHTML components. See droppable.txt for full documentation. Basic Integration: Template (My_Uploader.jqhtml):
Drop files here or click to upload
JavaScript (My_Uploader.js): class My_Uploader extends Jqhtml_Component { on_render() { // Handle dropped files via Droppable this.on('file-drop', (_, data) => { this._upload_files(data.files); }); // Handle click-to-upload this.$.on('click', () => this.$sid('file_input').click()); this.$sid('file_input').on('change', (e) => { this._upload_files(e.target.files); }); } async _upload_files(files) { for (let file of files) { // Validate if (file.size > 10 * 1024 * 1024) { Flash.error(`${file.name} exceeds 10 MB limit`); continue; } // Upload const formData = new FormData(); formData.append('file', file); const response = await $.ajax({ url: '/_upload', type: 'POST', data: formData, processData: false, contentType: false }); if (response.success) { this._add_to_list(response.attachment); } } } _add_to_list(attachment) { this.$sid('file_list').append( `
  • ${attachment.file_name}
  • ` ); } } SCSS (My_Uploader.scss): .My_Uploader { border: 2px dashed #ccc; padding: 20px; text-align: center; cursor: pointer; &.rsx-drop-active { border-color: #007bff; background: rgba(0, 123, 255, 0.05); } &.rsx-drop-target { border-style: solid; background: rgba(0, 123, 255, 0.15); } } SECURITY CONSIDERATIONS 1. Always validate files: - File type (MIME type validation) - File size (prevent DoS) - Filename sanitization (handled automatically) 2. Always require authentication: - Use #[Auth('Permission::authenticated()')] - Check user permissions for file access 3. Never trust user input: - Validate all parameters - Use Laravel validation rules 4. Rate limiting: - Add rate limiting to upload endpoints - Prevent abuse Example with rate limiting: #[Route('/files/upload', method: 'POST')] #[Auth('Permission::authenticated()')] #[Ajax_Endpoint] public static function upload(Request $request, array $params = []) { // Rate limit: 10 uploads per minute if (RateLimiter::tooManyAttempts('file-upload:' . RsxAuth::id(), 10)) { return [ 'success' => false, 'error' => 'Too many uploads. Please try again later.' ]; } RateLimiter::hit('file-upload:' . RsxAuth::id(), 60); // ... rest of upload logic } SEE ALSO file_upload.txt - Complete file upload system documentation droppable.txt - Global drag-and-drop file interception system model.txt - Model system documentation routing.txt - Route and endpoint documentation VERSION RSpade Framework 1.0 Last Updated: 2026-01-15