Hostingheaderbarlogoj
Join InMotion Hosting for $3.49/mo & get a year on Tuts+ FREE (worth $180). Start today.
Advertisement

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.


Advertisement