skip to Main Content

I am trying to set up a site so that users only have access to their own images and audio files. To to this I am using variables in the URL such as:

 <img src="/public/get_file.php?type=image&file=pic001.png" />

On the front-end I am using React JS (not sure if that is important to this issue). But on the backend, the PHP script will validate that the user is logged in (just by checking a simple SESSION variable) and look for that file in the user’s data directory (based on the user id in their SESSION variable). If it exists it will return it to the user using XSendFile.

The problem I am having is that every time that the user tries to access the files there is a bit of a delay before they load. This is telling me that they probably are not being cached by the browser.

Why are the files not getting cached? Does it have to do with the URL parameter or the use of PHP/XSendFile?

What can I do to allow my files (images/audio) to be cached?

Update

As requested here is my get_file.php:

<?php

        session_start();

        if (empty($_SESSION['auth']['id'])){
                exit;
        }

        require_once("../../api_modules/settings.php");

        $developer_id = $_SESSION['auth']['id'];

        $type = preg_replace('/[^-a-zA-Z0-9_]/', '', $_GET['type']);
        $file = preg_replace('/[^-a-zA-Z0-9_.]/', '', $_GET['file']);

        $file = preg_replace('/[.]{2,}/', '', $file);

        $project_id = preg_replace('/[^0-9]/', '', $_GET['project_id']);
        $developer_id = preg_replace('/[^0-9]/', '', $developer_id);

        if ($type == "image")
                $file = FILEPATH ."files/$developer_id/$project_id/images/thumbs/88x88/$file";
        else if ($type == "full_image")
                $file = FILEPATH ."files/$developer_id/$project_id/images/originals/$file";
        else if ($type == "audio"){
                $file = FILEPATH ."files/$developer_id/$project_id/audio/$file";
        }
        else {
                exit;
        }


        header("X-Sendfile: $file");
        header("Content-type: application/octet-stream");
        header('Content-Disposition: attachment; filename="' . basename($file) . '"');

4

Answers


  1. Try to use this kind of header (PHP code) with for example header('Cache-Control: must-revalidate'); for download a private files

    $mime_types = array(
            'pdf' => 'application/pdf',
            'txt' => 'text/plain',
            'html' => 'text/html',
            'exe' => 'application/octet-stream',
            'zip' => 'application/zip',
            'doc' => 'application/msword',
            'xls' => 'application/vnd.ms-excel',
            'ppt' => 'application/vnd.ms-powerpoint',
            'gif' => 'image/gif',
            'png' => 'image/png',
            'jpeg' => 'image/jpg',
            'jpg' => 'image/jpg',
            'php' => 'text/plain'
            );
    
    if ($mimeType == '') {
        $file_ext = strtolower(substr(strrchr($file_path.$file_name_gen, '.'), 1));
        if(array_key_exists($file_ext, $mime_types)) $mime_type = $mime_types[$file_ext];
        else $mime_type = 'application/force-download';
        }
    
    
    ob_start();
    header('Content-Disposition: attachment; filename="' . $file_name_gen . '"');
    header('Content-Type: '.$mime_type);
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Length: ' . filesize($file_path.$file_name));
    ob_clean();
    
    flush();
    readfile($file_path.$file_name);
    

    And use Cache control with a best option for your solution like

    Cache-Control: max-age=<seconds>
    Cache-Control: max-stale[=<seconds>]
    Cache-Control: min-fresh=<seconds>
    Cache-Control: no-cache 
    Cache-Control: no-store
    Cache-Control: no-transform
    Cache-Control: only-if-cached
    

    BTW. $file_name_gen is a genuine file name on the server and $file_name is a file name users see for more file privacy.

    Login or Signup to reply.
  2. For the record, this is mostly due to the fact the default value of session.cache_limiter is nocache.

    See http://php.net/manual/en/function.session-cache-limiter.php on how to set a different value that fits your needs.

    Login or Signup to reply.
  3. How do you generate the images? Do you have a public local directory or it is loaded by a CDN (remotely)?

    Maybe it can help: HOW TO CACHE IMAGES GENERATED BY PHP

    Login or Signup to reply.
  4. I think this is a duplicate.

    But since there is a bounty lets try to adapt your code together with the suggested solution there.

    You need to check that Apache has mod_expires and mod_headers enabled and working properly.

    And then before you start real output check if file was modified:

    ...
    $last_modified_time = filemtime($file); 
    $etag = md5_file($file);
    // always send headers
    header("Last-Modified: ".gmdate("D, d M Y H:i:s", $last_modified_time)." GMT"); 
    header("Etag: $etag"); 
    // exit if not modified
    if (@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $last_modified_time || 
        @trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag) { 
        header("HTTP/1.1 304 Not Modified"); 
        exit; 
    } else {
        header("X-Sendfile: $file");
        header("Content-type: application/octet-stream");
        header('Content-Disposition: attachment; filename="' . basename($file) . '"');
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search