Feb28

PHP Upload Script with Progress Bar

If you want to upload some files through a web interface using PHP, you might use a classic POST method to push the data to the action script. If you choose your file and submit the HTML form, the file is transfered to the server and moved into a temporary file. Further your action script can examine the file and copy it to a specific location.
This method works fine, but I has one essential disadvantage: The file has to be transfered completely before you can examine the filetype, filesize and other file information. Further when transfering the file, you have no information about the transfer status. Assume, you want to upload a huge file. After you submit the upload form, you’ll wait several minutes without any information.I found a solution which is working quiet well. I will post some examples and will desribe basic ideas about upload files using CGI PHP. Maybe you want to use this information to build your own upload script.

Using CGI PHP

There are different possibilities, you can run PHP. In general, PHP runs as module, e.g. in a apache web server. But there is another interesting method to run PHP. If you run a PHP script as CGI program, a new process is forked and the PHP interpreter is loaded into this process. This method allows us to run several PHP scripts concurrently.
Imagine, that we have a PHP script to transfer the file and periodically write out the size of transfered data into a temporary state file. Further another script exists to read out the state file information, calculate the percentage of transfered data and displays a cute progress bar. Now we can use the concurrency described above to let the two scripts run concurrently. So you see, the main idea is to separate the upload and the statistic script. While the upload script transfers data, the statistic script will show the progress bar.

Filetransfer Management

The idea of transfering is quit easy: We read a data package of a couple of bytes (e.g. 1024 bytes), examine it and save it in a temporary file. After we reach the end of file (eof), we move the temporary file to the destination file. You can get an idea of this transfer from the following code example:

#!/usr/bin/php
<?PHP // If the file size is too huge for uploading ... if($_ENV['CONTENT_LENGTH'] &gt; (1024^2) * 10)
{
     echo "Location: http://meinedomain.de/error.html\n\n";
     die(0);
}
$bytesTransfered = 0;
while(!feof(STDIN))
{
     $buffer = fread(STDIN,1024);
     $bytesTransfered += strlen($buffer);
     file_put_contents("/tmp/upload",$buffer,FILE_APPEND);
}
die(0);
?>

Note, that this script run as CGI program, like I desribed above.

As you see, I periodically read 1024 bytes from STDIN. This wrapps the input stream e.g. coming from a HTML input form. Let’s have a look at the input stream layout.

Further you are able, to validate the content length by using $_ENV[‘CONTENT_LENGTH’] at the beginning of the upload script, like I did on line 4.

Data Streaming

If you look at the uploaded file, you will see something like the following snippet I got after uploading a jpeg file:

-----------------------------1951833994121914805585029106
Content-Disposition: form-data; name="datei"; filename="sysarc.jpg"
Content-Type: image/jpeg
some binary or ASCII code
-----------------------------1951833994121914805585029106--

As you see, we got the basic information Content-Disposition and Content-Type at the beginning of the file stream. So the idea is to parse the first lines of an incoming transfer until the header is completely transfered. Further one can examine the header information and perhaps, you can stop the file transfer, e.g. if someone wants to upload a forbidden content type.

To sum up, we have an upload control script, which can hand over essential information (filetype, filename, filesize, transfered bytes) to another statistic script. It’s your turn to create a statistic script which analysis this information.

Avoiding Javascript

Now, you know the basic ideas about uploading files with pure PHP. You don’t need further client-side scripting languages like Javascript to use the upload script. Think about multiple frames (I know, that’s very old-school, but I found no other possibility). If your are using two frames, one frame can show the upload form and can call the upload script while the other frame can show the statistic page all the time. The statistic page refreshes itself every few seconds using the following html meta tag:

<meta http-equiv="refresh" content="5; URL=http://meinedomain.de/statistic.php">

If an upload begins, the upload script hands over upload information to the statistic script which creates an upload progress bar.

Leave a Reply

You must be logged in to post a comment.