Advertisement
News

How to Open Zip Files with PHP

by

ThemeForest has a nice feature; It allows the authors to upload zip files containing screenshots of their themes. A script then extracts these files and displays the images accordingly. Though I doubt that the developers used PHP to accomplish this task...that's what we're going to use!

What We Must Do

  • Create a simple upload form that allows the user to select a zip file.
  • Ensure that the user chooses a zip file, and then save it to as a unique file.
  • Extract the contents from the zip file and save them to a specific folder.
  • Delete the zip file and echo out the contents.

Step 1: Creating the Upload Form

Upload Form
  • Create a new file and save it as index.html in the root of your solution.
  • Next, we'll build a simple form. Paste the following in.
	<form enctype="multipart/form-data" action="" method="post">
		<input type="file" name="fupload" /><br />
		<input type="submit" value="Upload Zip File" />
	</form>

Any time that you'll be allowing your users to upload files, you must set the "enctype" of the form tag equal to "multipart/form-data". We'll next set the action equal to the same page, and the method equal to post.

Form HTML

To keep things as barebones as possible, we only have a file input and a submit button. Note that we've set the name of the file input to "fupload". We'll soon use this value to determine whether or not the page has posted back.

Step 2: Writing the PHP

At the very top of your index.php page, before the doctype has been declared, paste in the following:

<?php
if(isset($_FILES['fupload'])) {
	$filename = $_FILES['fupload']['name'];
	$source = $_FILES['fupload']['tmp_name'];
	$type = $_FILES['fupload']['type'];

Let's take it step by step.

  • If the input tag with a name of "fupload" is set, then run the following code, otherwise, do nothing.
  • Now we need to create a few variables. We need to capture the name of the file uploaded, the directory where the file is temporarily stored, and the type of file that was chosen.

Let's assume that a user chooses a file called "myPics.zip".

  • $_FILES['fupload']['name'] = "myPics.zip"
  • $_FILES['fupload']['tmp_name'] = some temporary directory on the server.
  • $_FILES['fupload']['type'] = "application/zip". (This value can change depending on which browser the file was uploaded from.
Contents of the FILES global array

Next, let's explode the file name into two pieces: the name, and the extension.

$name = explode('.', $filename);
$target = 'extracted/' . $name[0] . '-' . time() . '/';

Continuing with our example "myPics.zip" file - $name[0] will equal "myPics". $name[1] will equal "zip".

Next, we create a new variable called "$target". This will be the location that our file is saved to. In order to ensure that a different user withe the same "myPics" name doesn't have his files overwritten, we must make sure that we save the files to a unique location. To accomplish this task, we'll implement the "time()" function. If we append this value to the name of the uploaded file, we can be sure that we'll end up with a unique folder name!

$target = "extracted/myPics-02151985/"

Step 3: Ensure That a Zip File Was Selected

Immediately after your $target variable, paste in the following:

	$accepted_types = array('application/zip', 'application/x-zip-compressed', 'multipart/x-zip', 'application/s-compressed');

	foreach($accepted_types as $mime_type) {
		if($mime_type == $type) {
			$okay = true;
			break;
		}
	}
  • We start by creating an array called $accepted_types. A quick Google search for "zip mime types" will bring us four values: 'application/zip', 'application/x-zip-compressed', 'multipart/x-zip', 'application/s-compressed'. Each browser has its own way of registering the file type. We must be sure to check each value in such instances.
  • To do so, we check to see if any of the items in our array have the same value as "$_FILES['fupload']['type'] ". If they do, we'll set our handy-dandy "$okay" variable equal to 'true' - and can break out of the 'for' statement and rest assured that the user has in fact chosen a zip file.

Unfortunately, Safari and Chrome don't register a type for zip files. This creates a problem for us. After a bit of research, I wasn't able to find an easy solution - without using an extension (PEAR). Instead, we'll make sure that the file name at least ends in "zip". It should be noted that this isn't 100% safe. What if the user uploaded a different file type that ended in 'zip'? Feel free to offer recommendations! :)

$okay = strtolower($name[1]) == 'zip' ? true: false;
  • We'll use the ternary operator to keep this statement down to one line. If the extension of the filename ($name[1]) is equal to 'zip', we'll set $okay to true. Otherwise, it will be equal to 'false'.

Next, we'll check to see if $okay is false. If it is, we know that a 'zip' file was not chosen. In such cases, we'll tell PHP to die.

if(!$okay) {
  die("Please choose a zip file, dummy!");
}

Step 4: Saving the Zip File

mkdir($target);
$saved_file_location = $target . $filename;
	
if(move_uploaded_file($source, $saved_file_location)) {
  openZip($saved_file_location);
} else {
  die("There was a problem. Sorry!");
}
  • We first need to create the directory that we referenced with our $target variable. This can easily be done by using the "mkdir" function.
  • Next, let's try to move the uploaded file, from the temporary directory, to our $target directory. If that procedure was performed successfully, we'll call the "openZip" function.

Step 5: The openZip() Function

Create a new page and save it as "functions.php". Now add the following code:

Upload Form
<?php

function openZip($file_to_open) {
	global $target;
	
	$zip = new ZipArchive();
	$x = $zip->open($file_to_open);
	if($x === true) {
		$zip->extractTo($target);
		$zip->close();
		
		unlink($file_to_open);
	} else {
		die("There was a problem. Please try again!");
	}
}
?>

This function will accept one parameter: $file_to_open. This parameter will contain the location of the zip file that we're trying to extract!

  • To use the ZipArchive() class, you must first make sure that enable 'php_zip.dll' in your "php.ini" file. Simply search for that string, and then remove the semicolon.
    PHP.NET Zip info
  • With any class, we need to create a new instance of the object. We'll next call the "open" method and pass in the location of the file that should be opened. If performed successfully, this method will return 'true'. If that is the case, we'll extract the contents of the zip file to a folder. We already created the path to this folder when we created our $target variable. (Note - In order to access that variable, you need to add "global" in front of the variable. This will tell PHP to go outside of the current function and search for the $target variable.).
  • Once the files have been extracted, we'll delete the zip file - as we no longer need it!

Remember - we've created our new 'functions.php' file, but we need to include it! Append the following to the top of your 'index.php' page, just after the opening PHP tag.

require_once 'functions.php';

Echoing the Contents

Echoing out the contents

What we have so far works perfectly! But, just to give you some feedback, let's scan the new directory and echo out the contents. You should delete this next code block from your project. It's only for testing. If you do want to keep it, echo the information out within the body tag.

	$scan = scandir($target . $name[0]);
	
	print '<ul>';
	for ($i = 0; $i<count($scan); $i++) {
		if(strlen($scan[$i]) >= 3) {
			$check_for_html_doc = strpos($scan[$i], 'html');
			$check_for_php = strpos($scan[$i], 'php');
			
			if($check_for_html_doc === false && $check_for_php === false) {
				echo '<li>' . $scan[$i] . '</li>';
			} else {
				echo '<li><a href="' . $target . $name[0] . '/' . $scan[$i] . '">' . $scan[$i] . '</a></li>';
			}
		}
	}
	print '</ul>';
}

I won't go over this last part too much - as it's unnecessary. However, if you would like a full explanation, be sure to watch the associated screencast! To quickly sum it up, this last script scans our new directory and echos out an unordered list containing the contents.

Finished

Not too hard, eh? It's a neat feature that can easily be implemented into your projects. What are your thoughts on security when it comes to uploading zip files? How can we be sure that the contents of the zip file aren't harmful? I'd love to hear everyone's opinion on these security implications. If not handled properly, a hacker could cause serious harm to your server. Let's discuss it! It's possible to search for dangerous file types (.htaccess files) and delete them.

The Final Index.php Page

<?php

	include 'functions.php';

if(isset($_FILES['fupload'])) {
	$filename = $_FILES['fupload']['name'];
	$source = $_FILES['fupload']['tmp_name'];
	$type = $_FILES['fupload']['type']; 
	
	$name = explode('.', $filename); 
	$target = 'extracted/' . $name[0] . '-' . time() . '/';  
	
	// Ensures that the correct file was chosen
	$accepted_types = array('application/zip', 
                                'application/x-zip-compressed', 
                                'multipart/x-zip', 
                                'application/s-compressed');

	foreach($accepted_types as $mime_type) {
		if($mime_type == $type) {
			$okay = true;
			break;
		} 
	}
      
  //Safari and Chrome don't register zip mime types. Something better could be used here.
	$okay = strtolower($name[1]) == 'zip' ? true: false;

	if(!$okay) {
          die("Please choose a zip file, dummy!");       
	}
	
	mkdir($target);
	$saved_file_location = $target . $filename;
	
	if(move_uploaded_file($source, $saved_file_location)) {
		openZip($saved_file_location);
	} else {
		die("There was a problem. Sorry!");
	}
	
// This last part is for example only. It can be deleted. 
	$scan = scandir($target . $name[0]);
	
	print '<ul>';
	for ($i = 0; $i<count($scan); $i++) {
		if(strlen($scan[$i]) >= 3) {
			$check_for_html_doc = strpos($scan[$i], 'html');
			$check_for_php = strpos($scan[$i], 'php');
			
			if($check_for_html_doc === false && $check_for_php === false) {
				echo '<li>' . $scan[$i] . '</li>';
			} else {
				echo '<li><a href="' . $target . $name[0] . '/' . $scan[$i] . '">' . $scan[$i] . '</a></li>';
			}
		}
	}
	print '</ul>';
}

?>



<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 
<html>
  <head>
    <title>How to Upload and Open Zip Files With PHP</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <div id="container">
	<h1>Upload A Zip File</h1>
	<form enctype="multipart/form-data" action="" method="post">
		<input type="file" name="fupload" /><br />
		<input type="submit" value="Upload Zip File" />
	</form>

    </div><!--end container-->
  </body>
</html>
  • Subscribe to the NETTUTS RSS Feed for more daily web development tuts and articles.


Related Posts
  • Code
    PHP
    Object-Oriented Programming in WordPress: Control Structures IIWordpress oop
    WordPress is a powerful CMS that provides a flexible foundation on which to build both sites and applications. If you want to extend WordPress functionality, then you do so via plugins. Although there are a number of different ways to do that, this series has us taking a look at object-oriented programming specifically from the perspective of a beginner and in the context of WordPress. In this post, we're looking at a second set of control structures that allow us to control the flow of code throughout an application.Read More…
  • Code
    Web Development
    Refactoring Legacy Code: Part 2 - Magic Strings & ConstantsRefactoring wide retina preview
    Learn techniques for how to deal with complex and complicated unknown legacy code, how to understand it, and finally updating the Golden Master tests.Read More…
  • Code
    Theme Development
    Custom Controls in the Theme CustomizerTheme customizer custom control 400
    In the last article, we explored the advanced controls available in the Theme Customizer, and how to implement them. We’re going to look at how to create our own custom control, allowing you to choose which Category of Posts are displayed on the home page. To get started, download version 0.6.0 of our Theme Customizer Example.Read More…
  • Web Design
    eCommerce
    Getting Started With Liquid; Shopify's Template LanguageShopify 2 preview
    This tutorial will introduce Liquid, the Shopify template language. We'll look at the benefits of Liquid, discuss how it enables us to pull in and manipulate data from our stores and finally showcase a number of its key features that you will find yourself using daily in your Shopify theme development.Read More…
  • Code
    WordPress
    Mastering WordPress Meta Data: Working With LoopsMetadata
    In the first two parts of this series, we covered what meta data is in WordPress and how to work with the arrays that are typically returned. Now that you've learned to do the detective work necessary to find the structure of an array, it's time to learn to use loops to automate the process of outputting an array. Once you learn this important skill you will never have to write repetitive HTML markup again.Read More…
  • Code
    PHP
    Creating a Photo Tag Wall With Twilio Picture Messaging & PHPProcedural to oop php retina preview
    Twilio's recently announced Picture Messaging has vastly opened up what we can do with text messaging, now we can attach photos to our text messages and have them get used in different ways. In our case, we are going to build a Photo Tag Wall, which will contain photos linked to tags that will be displayed on a website.Read More…