Jump to content

Developer Documentation

core/FileStorage

What it is

FileStorage extensions allow you to define a type of file that will be stored, which in turn allows administrators determine where and how to store them (for instance, to store some files on Amazon S3 and some files locally in a specific directory). It is imperative that every type of file has its own storage extension to prevent files from getting orphaned later, or being removed by the software because the extension used does not properly map to where the files are being stored.

How to use

When you create a FileStorage extension, you will need to define 5 methods within the class.

    /**
     * Count stored files
     *
     * @return    int
     */
    public function count()

The count() method should return the number of files total that are currently being stored by the file storage extension. Often, this will be a database COUNT() query result.

    /**
     * Move stored files
     *
     * @param    int            $offset                    This will be sent starting with 0, increasing to get all files stored by this extension
     * @param    int            $storageConfiguration    New storage configuration ID
     * @param    int|NULL    $oldConfiguration        Old storage configuration ID
     * @throws    \UnderflowException                    When file record doesn't exist. Indicating there are no more files to move
     * @return    void|int                            An offset integer to use on the next cycle, or nothing
     */
    public function move( $offset, $storageConfiguration, $oldConfiguration=NULL )

The move method is called when files are being moved from one storage configuration (e.g. disk storage) to another (e.g. Amazon S3). The method will generally need to do the following:

  • Select the next row to be moved. Typically this will be a database query, using limit $offset, 1 (pull 1 file starting at $offset), with first() called against the query. This may result in an UnderflowException if there are no more rows to pull, however that is ok and will be caught by the move files process.
    $emoticon = \IPS\Db::i()->select( '*', 'core_emoticons', array(), 'id', array( $offset, 1 ) )->first();
  • Next, you will load the file from the old storage configuration, and move it to the new configuration.
    $file = \IPS\File::get( $oldConfiguration ?: 'core_Emoticons', $emoticon['image'] )->move( $storageConfiguration );
    This process can result in an exception if the file cannot be found (for instance), which should be caught and ignored (as the underlying error is already logged).
  • Finally, you will typically need to update the database row to reflect the new location of the file.
                if ( (string) $file != $row['location'] )
                {
                    \IPS\Db::i()->update( 'database_table', array( 'location' => (string) $file ), array( 'id=?', $row['id'] ) );
                }
     

You can optionally return an integer offset if you prefer, which can allow you to return (for example) the last ID processed and start at that ID on the next cycle, instead of using limit( $offset, 1 ). This will provide much better performance if the table is expected to get very large.

    /**
     * Check if a file is valid
     *
     * @param    string    $file        The file path to check
     * @return    bool
     */
    public function isValidFile( $file )

The isValidFile() method is used to determine if a file is valid for the storage engine. You will want to query your database table (or otherwise check where you are storing file mappings) to determine if the file is valid or not. If so, return TRUE, otherwise return FALSE.

    /**
     * Delete all stored files
     *
     * @return    void
     */
    public function delete()

The delete method is used to delete all files in the storage extension. You will need to loop over all of the files and call the delete method against the \IPS\File object representing the file.

An example for the image proxy would be

    /**
     * Delete all stored files
     *
     * @return    void
     */
    public function delete()
    {
        foreach( \IPS\Db::i()->select( '*', 'core_image_proxy', 'location IS NOT NULL' ) as $cache )
        {
            try
            {
                \IPS\File::get( 'core_Imageproxycache', $cache['location'] )->delete();
            }
            catch( \Exception $e ){}
        }
    }

    /**
     * Fix all URLs
     *
     * @param    int            $offset                    This will be sent starting with 0, increasing to get all files stored by this extension
     * @return void
     */
    public function fixUrls( $offset )

Finally, the fixUrls() method is designed to loop through all files and fix the URLs that are stored for the files. There is a handy method in \IPS\File named repairUrl() which will do the bulk of the work for you. The general template is as follows

    /**
     * Fix all URLs
     *
     * @param    int            $offset                    This will be sent starting with 0, increasing to get all files stored by this extension
     * @return void
     */
    public function fixUrls( $offset )
    {
        $cache = \IPS\Db::i()->select( '*', 'database_table', array(), 'id ASC', array( $offset, 1 ) )->first();
        try
        {
            if ( $new = \IPS\File::repairUrl( $cache['location'] ) )
            {
                \IPS\Db::i()->update( 'database_table', array( 'location' => (string) $new ), array( 'id=?', $cache['location'] ) );
            }
        }
        catch( \Exception $e )
        {
            /* Any issues are logged and the \IPS\Db::i()->update not run as the exception is thrown */
        }
    }


  Report Document


×
×
  • Create New...