Skip to content

<div style="display: none;" hidden="true" aria-hidden="true">Are you an LLM? You can read better optimized documentation at /guides/Storage.md for this page in Markdown format</div>

Storage Refactoring and S3 Integration ​

API ​

Methods ​

  • Storage::get($path)
  • Storage::put()
  • Storage::getStream($path)
  • Storage::putStream($path, $stream, $options = [])
  • Storage::listContents($directory = '')
  • Storage::move($from, $to)
  • Storage::delete()
  • Path normalization in all methods

Helper ​

  • storage() with default type files and default disk local if not set in .env

Storage Structure ​

The folders below (except user_langs) were moved from public/files. user_langs is the generated language files which were originally in storage root folder. All moved folders in storage shouldn't be public. They should be moved before running MoveLocalToS3 Command

storage/
├── forms/           # Moved from public/files/forms
├── imports/         # Temporary storage for imports
│   └── temp/        # Temporary unzipped files
├── invoices/        # Moved from public/files/invoices
├── user_langs/      # User language files
└── vouchers/        # Voucher files
    └── dhl/         # DHL-specific vouchers

Configuration - Environment Variables ​

Configuration for new types and disks is in application/config/storage.php

Currently, types files and iqvia are already supported. When not set in .env the default values are:

#FILES_STORAGE_DRIVER=local
#IQVIA_STORAGE_DISK=sftp

Local Storage ​

For local storage and type files there is nothing to setup FILES_STORAGE_DISK can be set to local or empty which is the default in application/config/storage.php

#FILES_STORAGE_DISK=local

SFTP Storage ​

#<STORAGE_TYPE>_SFTP_HOST=
#<STORAGE_TYPE>_SFTP_USERNAME=
#<STORAGE_TYPE>_SFTP_PASSWORD=
#<STORAGE_TYPE>_SFTP_PORT=22
#<STORAGE_TYPE>_SFTP_ROOT=/
#<STORAGE_TYPE>_SFTP_TIMEOUT=30
#<STORAGE_TYPE>_SFTP_PRIVATE_KEY=null
#<STORAGE_TYPE>_SFTP_PASSPHRASE=null

S3 Storage ​

#<STORAGE_TYPE>_S3_ACCESS_KEY_ID=
#<STORAGE_TYPE>_S3_SECRET_ACCESS_KEY=
#<STORAGE_TYPE>_S3_DEFAULT_REGION=
#<STORAGE_TYPE>_S3_BUCKET=
#<STORAGE_TYPE>_S3_ENDPOINT=
#<STORAGE_TYPE>_S3_AWS_USE_PATH_STYLE_ENDPOINT=
#<STORAGE_TYPE>_S3_URL=
#<STORAGE_TYPE>_S3_PREFIX=

AdvUploader Updated ​

Storage is hooked on AdvUploader class. The validation has been extracted from codeigniter to standalone class The persistence of files is done through storage helper

CI_Upload can be used for not public files, although a local driver for the specific folder could be created

Migration Process ​

Prepare code updates for migration ​

This will be handled after merging from master to client branch.

If there are folders in public/files that should not be public update UpgradeStorage like this

php
public function execute()
{
    parent::execute();

    //and add commands for moving additional folders
    (new InternalMoveFolder())->execute('files/the-folder', '../storage/the-folder');
}

and create the-folder in /storage

Directory guard stubs are preserved automatically ​

InternalMoveFolder never moves directory scaffolding out of the web root — the 403 guard index.html that ships in each public/files/* folder, plus .gitignore and .gitkeep. A folder pair you add above gets that behaviour for free; you do not need to protect your own stub.

The 403 guard is recognised by content, not by name: an index.html of at most 4096 bytes containing the string 403 Forbidden. A genuine index.html upload is therefore still moved normally, at any nesting depth. execute() also remediates the older, unfiltered behaviour: a recognised guard already sitting at the destination is moved back to the source, or deleted when the source already has one. The destination sweep considers index.html only — it never touches the tracked .gitkeep that every /storage folder ships.

To add your own filenames to the skip list, redeclare the constant in application/libraries/StorageJobs/InternalMoveFolder.php using the spread form:

php
class InternalMoveFolder extends AdvInternalMoveFolder
{
    protected const GUARD_FILES = [...parent::GUARD_FILES, 'client-marker.txt'];
}

Do not use array_merge() here. protected const GUARD_FILES = array_merge(parent::GUARD_FILES, ['client-marker.txt']); is a compile-time fatal — Constant expression contains invalid operations — which takes the whole file down, not just the job. The spread form is the only composition that compiles.

Two more rules ride with the constant: the base class reads it as static::GUARD_FILES, so a subclass redeclaration really does win (never change that to self::, which silently returns the parent list); and the constant must stay protected or wider and must not be marked final — narrowing it to private in a subclass is a fatal, and final closes the seam.

If your stub is worded differently and does not contain 403 Forbidden, override protected function isDirectoryIndexGuard(string $path): bool instead of adding index.html to GUARD_FILES — adding the name there would also stop genuine index.html uploads from being moved.

Execute Migration Process ​

  • Backup existing files
  • Verify storage configuration
  • Migration executes UpgradeStorage which moves folders that shouldn't be public in /storage folder

Move public files to S3 ​

  1. run php cli.php job/CopyLocalToS3
  2. when ready setup nginx or apache (whichever is front) as reverse proxy for /files folder as described in docs/servers/ApacheNginxReverseProxy
  3. set env var FILES_STORAGE_DISK=s3
  4. set up mediastream config application/config/mediastream/services.yaml to have for key app.imageFetch.job.resolveFromStorageImageFetchJob: the following
    app.imageFetch.job.resolveFromStorageImageFetchJob:
    class: EcommerceN\MediaStream\Domain\ImageFetch\Job\Implementation\ProxyCurlFromWebImageFetchJob
    arguments:
      $options:
        proxyTemplatePath: '%env(FILES_S3_URL)%/files/{type}/{fileName}'
        fetchedTemplatePath: "{locationRoot}/{typeSubDir}/{fileName}"
        fetchedStorageLocation: ../storage/tmpfiles
        headers:
          CURLOPT_RETURNTRANSFER: 1
  5. ensure images are being served from s3 bucket and run php cli.php job/DeleteLocalFiles

Getting Demo Files ​

Clients that need files from demo.ecommercen.com for a new site can run php cli.php job/CopyDemoFiles (@TODO) which will copy demo files to s3 bucket for client