Every once in a while you need to do some edge testing when writing code that handles files. One way to fix this is to use dummy files at specific sizes. The following code is an example on how to write a Dummy File Generator in PHP. You can also find this code on github

First let’s write a function to handle this. This function takes a size and a unit, e.g. size 150 and unit 2. This will be read as 150MB. First we convert the input parameters to bytes. We then check that there isn’t already a dummy file generated for the selected size, this is so that you simply can reuse a previous file next time you need it instead of generate an identical file. We also make sure that the size is a value between 0 bytes and whatever we set as a maximum size (in this case 200MB). If this goes through we call the writeDummyFile function.

/**
 * Creates a dummy file of a defined size
 *
 * @param int $size
 * @param int $unit 0=B, 1=kB, 2=MB (default), 3=GB
 */
function generateFile(int $size, int $unit = 2): void
{
    $size = floor($size * pow(1024, $unit));

    if ( ! file_exists($size.'.txt.gz') && $size >= 0 && $size <= 200 * 1024 * 1024) {
        writeDummyFile($size);
        writeZipFile($size);

        unlink($size.'.txt'); // remove the original dummy file after finished compression
    }
}

This function is pretty simple, we simply create a new file on disk, name it as the size in bytes and the filetype txt. The filetype can be anything but setting it as txt will help Windows users to open it easily. We then place a character (a null value in this example but it could really be anything) at the position of the value of size variable minus 1. We then write to this file. The file is now filled with null values at the exact size you chose.

/**
 * Create the a dummy file on disk
 *
 * @param $size
 *
 * @return void
 */
function writeDummyFile($size): void
{
    $fp = fopen("$size.txt", "w");

    // Put a character at the end (the size in bytes - 1) of the file
    fseek($fp, $size - 1, SEEK_CUR);
    fwrite($fp, chr(0));

    fclose($fp);
}

When the above function finishes the parent function will call the writeZipFile function. Here we start with reading the file we created in the previous function compress the data at maximum level, since the files only consist of null values even a file at about 200MB will only be a few hundred kB when compressed. When we have the compressed data we write that to a new file. and let the parent function remove the original dummy file.

/**
 * Compress the dummy file to make it take up far less disk space
 *
 * @param $size
 */
function writeZipFile($size): void
{
    $data = implode("", file("$size.txt")); // Read data from the dummy file
    $compressedData = gzencode($data, 9); // Use maximum compression level

    //Write the compressed data
    $fp = fopen("$size.txt.gz", "w");
    fwrite($fp, $compressedData);

    fclose($fp);
}

That’s it, if you want you can write a few lines that send the compressed file to the browser. This code simply gets the MIME type for the file, sets the appropriate headers for telling the browser what to do with the file we’re sending.

/**
 * Send the file to the browser
 *
 * @param int $size
 */
function download(int $size): void
{
    $type = mime_content_type("$size.txt.gz");

    // Send file headers
    header("Content-type: $type");
    header("Content-Disposition: attachment;filename=\"$size.txt.gz\"");
    header("Content-Transfer-Encoding: binary");
    header("Pragma: no-cache");
    header("Expires: 0");
    // Send the file contents.
    set_time_limit(0);
    readfile("$size.txt.gz");
}

Finally, let’s put this in a class and do some minor tweaking for legibility.

<?php

namespace CodeCauldron\Tools\File;

class DummyFileGenerator
{

    const MAX_SIZE = 200 * 1024 * 1024; // 200MB

    const B = 0;

    const KB = 1;

    const MB = 2;

    const GB = 3;

    /**
     * Creates a dummy file of a defined size
     *
     * @param int $size
     * @param int $unit 0=B, 1=kB, 2=MB (default), 3=GB
     */
    public function generateFile(int $size, int $unit = 2): void
    {
        $size = floor($size * pow(1024, $unit));

        if ($size >= 0 && $size <= DummyFileGenerator::MAX_SIZE) {
            if (! file_exists("$size.txt.gz")) {
                $this->writeDummyFile($size);
                $this->writeZipFile($size);

                unlink("$size.txt"); // remove the original dummy file after finished compression
            }
            $this->download($size);
        }
    }

    /**
     * Create the a dummy file on disk
     *
     * @param int $size
     *
     * @return void
     */
    private function writeDummyFile(int $size): void
    {
        $fp = fopen("$size.txt", "w");

        // Put a character at the end (the size in bytes - 1) of the file
        fseek($fp, $size - 1, SEEK_CUR);
        fwrite($fp, 'a');

        fclose($fp);
    }

    /**
     * Compress the dummy file to make it take up far less disk space
     *
     * @param int $size
     */
    private function writeZipFile(int $size): void
    {
        $data           = implode("", file("$size.txt")); // Read data from the dummy file
        $compressedData = gzencode($data, 9); // Use maximum compression level

        //Write the compressed data
        $fp = fopen("$size.txt.gz", "w");
        fwrite($fp, $compressedData);

        fclose($fp);
    }

    /**
     * Send the file to the browser
     *
     * @param int $size
     */
    private function download(int $size): void
    {
        $type = mime_content_type("$size.txt.gz");

        // Send file headers
        header("Content-type: $type");
        header("Content-Disposition: attachment;filename=\"$size.txt.gz\"");
        header("Content-Transfer-Encoding: binary");
        header("Pragma: no-cache");
        header("Expires: 0");
        // Send the file contents.
        set_time_limit(0);
        readfile("$size.txt.gz");
    }
}

To use this class in your projects simply do something like this. The example below will generate a 20kB dummy file.

<?php
require_once "DummyFileGenerator.php";

use CodeCauldron\Tools\File\DummyFileGenerator;

$gen = new DummyFileGenerator();
$gen->generateFile(20, DummyFileGenerator::KB);

When you have gotten hold of the compressed file, either by finding it on disk or by downloading it, simply decompress it and use the txt file.


Featured image by Gerd Altmann from Pixabay

Published by Jimmie

Jimmie is a full stack developer with over 13 years working experience in building web based solutions and over 25 years of programming experience over all. Jimmie has worked with a slew programming languages over the years, but his main focus is usually on web frameworks such as Symfony,  Angular, and Spring Boot.

Leave a comment

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: