Skip to content

File Uploads & Storage Management ​

StruxJS provides a file handling engine with magic-byte MIME type inspection, file validation rules, public storage linking, and storage permission management.


File Upload Handling ​

Access uploaded files using request.file() or request.files() in Controller action methods.

Single File Upload ​

typescript
import { Request, Response } from "struxjs";

export class ProfileController {
    public async uploadAvatar(request: Request, response: Response) {
        // Validate incoming file
        await request.validate({
            avatar: "required|file|image|max:2048" // Max 2MB authentic image
        });

        const avatar = request.file("avatar");

        if (avatar && avatar.isValid()) {
            // Save file into storage/app/public/avatars
            const publicPath = await avatar.store("avatars");
            
            return response.json({
                success: true,
                url: `/${publicPath}` // Returns: "/storage/avatars/a1b2c3.jpg"
            });
        }

        return response.status(400).json({ error: "No valid file uploaded" });
    }
}

Multiple File Uploads ​

Use request.files("fieldname") to retrieve an array of UploadedFile instances:

typescript
export class GalleryController {
    public async uploadPhotos(request: Request, response: Response) {
        const photos = request.files("photos");
        const savedPaths: string[] = [];

        for (const photo of photos) {
            if (photo.isValid()) {
                const path = await photo.store("gallery");
                savedPaths.push(path);
            }
        }

        return response.json({ savedPaths });
    }
}

UploadedFile API Reference ​

Each uploaded file is wrapped in an UploadedFile instance:

Properties ​

  • file.originalName: Original client-side filename (e.g. "portrait.jpg").
  • file.filename: Temporary uploaded filename.
  • file.mimeType: Client-reported MIME type string (e.g. "image/jpeg").
  • file.size: Uploaded file size in bytes.
  • file.buffer: Raw binary Buffer object.

Methods ​

file.store(targetDir?, customFilename?) ​

Saves the uploaded file into storage/app/public/${targetDir} and returns the web-accessible relative path (e.g. "storage/avatars/filename.png"). If customFilename is omitted, a random hexadecimal filename is generated.

typescript
const filePath = await file.store("avatars");

file.storeAs(targetDir, customFilename) ​

Saves the file into storage/app/public/${targetDir} with a specified filename:

typescript
const filePath = await file.storeAs("documents", "User_Contract_42.pdf");

file.realMimeType() ​

Inspects the binary magic bytes of the file buffer to determine its authentic MIME type, protecting against file extension spoofing.

typescript
const mime = file.realMimeType(); // e.g. "image/png"

file.realExtension() ​

Returns the real file extension detected from binary magic bytes (e.g. "png", "jpg", "pdf", "zip").

typescript
const ext = file.realExtension();

file.isFakeExtension() ​

Returns true if the client-provided file extension does not match the actual binary content (e.g. an executable .exe file renamed to .png).

typescript
if (file.isFakeExtension()) {
    throw new Error("File extension spoofing detected.");
}

file.isValid() ​

Returns true if the file buffer is non-empty and valid.


By default, files saved using file.store() are placed inside storage/app/public/. To make these files publicly accessible via the web server at http://localhost:3000/storage/..., create a symbolic link connecting public/storage to storage/app/public.

Run the CLI command:

bash
npx strux storage:link

Or via bootstrap CLI:

bash
node bootstrap-cli.js storage:link

This command creates a symlink:

public/storage ──> storage/app/public

Once linked, a file stored at storage/app/public/avatars/photo.png is served at:

http://localhost:3000/storage/avatars/photo.png

Production Permissions & Chmod Guide ​

On Linux and production servers (Ubuntu, Nginx, Apache), the web server process user (such as www-data or nginx) must have write permissions to storage/ and bootstrap/cache/ directories to handle file uploads, session files, caches, and logs.

1. Set Directory Ownership ​

Set the web server user (www-data) as owner of the application storage and cache directories:

bash
sudo chown -R www-data:www-data storage

If your user needs write access alongside www-data, add your user to the web server group:

bash
sudo usermod -a -G www-data $USER
sudo chown -R $USER:www-data storage

2. Set Directory & File Permissions (chmod) ​

Set read, write, and execute permissions (775 for directories, 664 for files):

bash
# Set directory permissions
sudo find storage -type d -exec chmod 775 {} \;

# Set file permissions
sudo find storage -type f -exec chmod 664 {} \;

Ensure the public/ directory allows symbolic link traversal:

bash
chmod 755 public

Released under the MIT License.