.htaccess

RewriteRule ^images\/downloads\/.*$ readmedia.php [L]

Joomla 3

https://gist.github.com/SniperSister/3cbc2055282c08ed43d4682e4fd36498

<?php
/**
 * @version    1.0.0
 * @package    Readmedia
 * @copyright  Copyright (C) 2018 David Jardin - djumla GmbH
 * @license    GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
 * @link       http://www.djumla.de
 */

/* Initialize Joomla framework */
define('_JEXEC', 1);

// Load system defines
if (file_exists(dirname(__FILE__) . '/defines.php'))
{
    require_once dirname(__FILE__) . '/defines.php';
}

if (!defined('_JDEFINES'))
{
    define('JPATH_BASE', dirname(__FILE__));
    require_once JPATH_BASE . '/includes/defines.php';
}

require_once JPATH_LIBRARIES . '/import.legacy.php';
require_once JPATH_LIBRARIES . '/cms.php';

// Load the configuration
require_once JPATH_CONFIGURATION . '/configuration.php';

/* Create the Application */
$mainframe = JFactory::getApplication('site');
/**************************************************/
// Your code starts here...
/**************************************************/

if (!JFactory::getUser()->id)
{
    header("HTTP/1.0 403 access denied");

    echo "Access denied";

    exit();
}

$requestedFile = str_replace(JUri::root(), "", JUri::current());
$location = dirname(__FILE__) . "/" . $requestedFile;

if (!file_exists($location))
{
    header("HTTP/1.0 404 file not found");

    echo "File not found";

    exit();
}

$size = filesize($location);
$time = date('r', filemtime($location));

$fm = @fopen($location, 'rb');

if (!$fm)
{
    header("HTTP/1.0 505 Internal server error");

    exit();
}

$begin = 0;
$end = $size;

if (isset($_SERVER['HTTP_RANGE']))
{
    if (preg_match('/bytes=\h*(\d+)-(\d*)[\D.*]?/i', $_SERVER['HTTP_RANGE'], $matches))
    {
        $begin = intval($matches[0]);

        if (!empty($matches[1]))
        {
            $end = intval($matches[1]);
        }
    }
}

if ($begin > 0 || $end < $size)
{
    header('HTTP/1.0 206 Partial Content');
}
else
{
    header('HTTP/1.0 200 OK');
}

header("Content-Type: " . mime_content_type($location));
header('Cache-Control: public, must-revalidate, max-age=0');
header('Pragma: no-cache');
header('Accept-Ranges: bytes');
header('Content-Length:' . ($end - $begin));
header("Content-Range: bytes $begin-$end/$size");
header("Content-Disposition: inline; filename=" . basename($location));
header("Content-Transfer-Encoding: binary\n");
header("Last-Modified: $time");
header('Connection: close');

$cur = $begin;
fseek($fm, $begin, 0);

while (!feof($fm) && $cur < $end && (connection_status() == 0))
{
    print fread($fm, min(1024 * 16, $end - $cur));
    $cur += 1024 * 16;
}

Joomla 4

https://gist.github.com/SniperSister/5a31e989da962099b517bf14fb978336

<?php
/**
 * @version    1.0.0
 * @package    Readmedia
 * @copyright  Copyright (C) 2018 David Jardin - djumla GmbH
 * @license    GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
 * @link       http://www.djumla.de
 */

/* Initialize Joomla framework */
define('_JEXEC', 1);

// Load system defines
if (file_exists(dirname(__FILE__) . '/defines.php'))
{
    require_once dirname(__FILE__) . '/defines.php';
}

if (!defined('_JDEFINES'))
{
    define('JPATH_BASE', dirname(__FILE__));
    require_once JPATH_BASE . '/includes/defines.php';
}

require_once JPATH_BASE . '/includes/framework.php';


/* Create the Application */
$container = \Joomla\CMS\Factory::getContainer();

$container->alias('session.web', 'session.web.site')
    ->alias('session', 'session.web.site')
    ->alias('JSession', 'session.web.site')
    ->alias(\Joomla\CMS\Session\Session::class, 'session.web.site')
    ->alias(\Joomla\Session\Session::class, 'session.web.site')
    ->alias(\Joomla\Session\SessionInterface::class, 'session.web.site');

// Instantiate the application.
$app = $container->get(\Joomla\CMS\Application\SiteApplication::class);

// Set the application as global app
\Joomla\CMS\Factory::$application = $app;

// Actual permission check starts here

if (!\Joomla\CMS\Factory::getUser()->id)
{
    header("HTTP/1.0 403 access denied");

    echo "Access denied";

    exit();
}

$requestedFile = str_replace(JUri::root(), "", JUri::current());
$location = dirname(__FILE__) . "/" . $requestedFile;

if (!file_exists($location))
{
    header("HTTP/1.0 404 file not found");

    echo "File not found";

    exit();
}

$size = filesize($location);
$time = date('r', filemtime($location));

$fm = @fopen($location, 'rb');

if (!$fm)
{
    header("HTTP/1.0 505 Internal server error");

    exit();
}

$begin = 0;
$end = $size;

header('HTTP/1.0 200 OK');
header("Content-Type: " . mime_content_type($location));
header('Cache-Control: public, must-revalidate, max-age=0');
header('Pragma: no-cache');
header("Content-Disposition: inline; filename=" . basename($location));
header("Content-Transfer-Encoding: binary\n");
header("Last-Modified: $time");
header('Connection: close');

$cur = $begin;
fseek($fm, $begin, 0);

while (!feof($fm) && $cur < $end && (connection_status() == 0))
{
    print fread($fm, min(1024 * 16, $end - $cur));
    $cur += 1024 * 16;
}