Making a temporary dir for unpacking a zipfile into

后端 未结 7 544
心在旅途
心在旅途 2020-12-29 23:05

I have a script that checks a zipfile containing a number of matching PDF+textfiles. I want to unpack, or somehow read the textfiles from the zipfile, and just pick out some

相关标签:
7条回答
  • 2020-12-29 23:20

    I wanted to add a refinement to @Mario Mueller's answer, as his is subject to possible race conditions, however I believe the following should not be:

    function tempdir(int $mode = 0700): string {
        do { $tmp = sys_get_temp_dir() . '/' . mt_rand(); }
        while (!@mkdir($tmp, $mode));
        return $tmp;
    }
    

    This works because mkdir returns false if $tmp already exists, causing the loop to repeat and try another name.

    Note also that I've added handling for $mode, with a default that ensures the directory is accessible to the current user only, as mkdir's default is 0777 otherwise.

    It is strongly advised that you use a shutdown function to ensure the directory is removed when no longer needed, even if your script exits by unexpected means*. To facilitate this, the full function that I use does this automatically unless the $auto_delete argument is set to false.

    // Deletes a non-empty directory
    function destroydir(string $dir): bool { 
        if (!is_dir($dir)) { return false; }
    
        $files = array_diff(scandir($dir), ['.', '..']);
        foreach ($files as $file) {
            if (is_dir("$dir/$file")) { destroydir("$dir/$file"); }
            else { unlink("$dir/$file"); }
        }
        return rmdir($dir); 
    }
    
    function tempdir(int $mode = 0700, bool $auto_delete = true): string {
        do { $tmp = sys_get_temp_dir() . '/' . mt_rand(); }
        while (!@mkdir($tmp, $mode));
    
        if ($auto_delete) {
            register_shutdown_function(function() use ($tmp) { destroydir($tmp); });
        }
        return $tmp;
    }
    

    This means that by default any temporary directory created by tempdir() will have permissions of 0700 and will be automatically deleted (along with its contents) when your script ends.

    NOTE: *This may not be the case if the script is killed, for this you might need to look into registering a signal handler as well.

    0 讨论(0)
  • 2020-12-29 23:26

    Another possibility is to use the temporal file as a kind of semaphore to guarantee the unicity of the directory name. Then, create a directory whose name is based on the file name.

    define ('TMP_DIR', '/tmp'); // sys_get_temp_dir() PHP 5 >= 5.2.1
    define ('TMP_DIR_PREFIX', 'tmpdir_');
    define ('TMP_DIR_SUFFIX', '.d');
    
    /* ************************************************************************** */
    
    function createTmpDir() {
      $tmpFile = tempnam(TMP_DIR, TMP_DIR_PREFIX);
      $tmpDir = $tmpFile.TMP_DIR_SUFFIX;
      mkdir($tmpDir);
      return $tmpDir;
    }
    
    function rmTmpDir($tmpDir) {
      $offsetSuffix = -1 * strlen(TMP_DIR_SUFFIX);
      assert(strcmp(substr($tmpDir, $offsetSuffix), TMP_DIR_SUFFIX) === 0);
      $tmpFile = substr($tmpDir, 0, $offsetSuffix);
    
      // Removes non-empty directory
      $command = "rm -rf $tmpDir/";
      exec($command);
      // rmdir($tmpDir);
    
      unlink($tmpFile);
    }
    
    /* ************************************************************************** */
    
    0 讨论(0)
  • 2020-12-29 23:28

    There are a lot of overkill answers to this question. One simple answer would be:

    $tempdir = tempnam(sys_get_temp_dir()) . 'dir';
    mkdir($tempdir);
    
    1. Obtain a temporary file name.
    2. Create the directory (append a suffix to temp file, to avoid file name collision.)
    3. Done.
    0 讨论(0)
  • 2020-12-29 23:29

    So I first found a post by Ron Korving on PHP.net, which I then modified to make a bit safer (from endless loops, invalid characters, and unwritable parent dirs) and use a bit more entropy.

    <?php
    /**
     * Creates a random unique temporary directory, with specified parameters,
     * that does not already exist (like tempnam(), but for dirs).
     *
     * Created dir will begin with the specified prefix, followed by random
     * numbers.
     *
     * @link https://php.net/manual/en/function.tempnam.php
     *
     * @param string|null $dir Base directory under which to create temp dir.
     *     If null, the default system temp dir (sys_get_temp_dir()) will be
     *     used.
     * @param string $prefix String with which to prefix created dirs.
     * @param int $mode Octal file permission mask for the newly-created dir.
     *     Should begin with a 0.
     * @param int $maxAttempts Maximum attempts before giving up (to prevent
     *     endless loops).
     * @return string|bool Full path to newly-created dir, or false on failure.
     */
    function tempdir($dir = null, $prefix = 'tmp_', $mode = 0700, $maxAttempts = 1000)
    {
        /* Use the system temp dir by default. */
        if (is_null($dir))
        {
            $dir = sys_get_temp_dir();
        }
    
        /* Trim trailing slashes from $dir. */
        $dir = rtrim($dir, DIRECTORY_SEPARATOR);
    
        /* If we don't have permission to create a directory, fail, otherwise we will
         * be stuck in an endless loop.
         */
        if (!is_dir($dir) || !is_writable($dir))
        {
            return false;
        }
    
        /* Make sure characters in prefix are safe. */
        if (strpbrk($prefix, '\\/:*?"<>|') !== false)
        {
            return false;
        }
    
        /* Attempt to create a random directory until it works. Abort if we reach
         * $maxAttempts. Something screwy could be happening with the filesystem
         * and our loop could otherwise become endless.
         */
        $attempts = 0;
        do
        {
            $path = sprintf('%s%s%s%s', $dir, DIRECTORY_SEPARATOR, $prefix, mt_rand(100000, mt_getrandmax()));
        } while (
            !mkdir($path, $mode) &&
            $attempts++ < $maxAttempts
        );
    
        return $path;
    }
    ?>
    

    So, let's try it out:

    <?php
    echo "\n";
    $dir1 = tempdir();
    echo $dir1, "\n";
    var_dump(is_dir($dir1), is_writable($dir1));
    var_dump(rmdir($dir1));
    
    echo "\n";
    $dir2 = tempdir('/tmp', 'stack_');
    echo $dir2, "\n";
    var_dump(is_dir($dir2), is_writable($dir2));
    var_dump(rmdir($dir2));
    
    echo "\n";
    $dir3 = tempdir(null, 'stack_');
    echo $dir3, "\n";
    var_dump(is_dir($dir3), is_writable($dir3));
    var_dump(rmdir($dir3));
    ?>
    

    Result:

    /var/folders/v4/647wm24x2ysdjwx6z_f07_kw0000gp/T/tmp_900342820
    bool(true)
    bool(true)
    bool(true)
    
    /tmp/stack_1102047767
    bool(true)
    bool(true)
    bool(true)
    
    /var/folders/v4/647wm24x2ysdjwx6z_f07_kw0000gp/T/stack_638989419
    bool(true)
    bool(true)
    bool(true)
    
    0 讨论(0)
  • 2020-12-29 23:30

    The "mkdir" function raises a warning if the directory already exists, so you can catch this using "@mkdir" and avoid any race condition:

    function tempDir($parent = null)
    {
        // Prechecks
        if ($parent === null) {
            $parent = sys_get_temp_dir();
        }
        $parent = rtrim($parent, '/');
        if (!is_dir($parent) || !is_writeable($parent)) {
            throw new Exception(sprintf('Parent directory is not writable: %s', $parent));
        }
    
        // Create directory
        do  { 
            $directory = $parent . '/' . mt_rand();
            $success = @mkdir($directory);
        }
        while (!$success);
    
        return $directory; 
    }
    
    0 讨论(0)
  • 2020-12-29 23:40

    quite easy (I took partly it from the PHP manual):

    <?php
    
    function tempdir() {
        $tempfile=tempnam(sys_get_temp_dir(),'');
        // you might want to reconsider this line when using this snippet.
        // it "could" clash with an existing directory and this line will
        // try to delete the existing one. Handle with caution.
        if (file_exists($tempfile)) { unlink($tempfile); }
        mkdir($tempfile);
        if (is_dir($tempfile)) { return $tempfile; }
    }
    
    /*example*/
    
    echo tempdir();
    // returns: /tmp/8e9MLi
    

    See: https://www.php.net/manual/en/function.tempnam.php

    Please look at Will's solution below.

    => My answer should not be the accepted answer anymore.

    0 讨论(0)
提交回复
热议问题