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