logo
backtop

Have we made any progress?

(Posted 18:08:00 on 22nd July 2007 by Rag)
A couple of week's back, a friend of mine asked if there was any way he could send me a file. Not for the site, just a video. So I created a quick form that would allow him to submit a file. Not a problem, in fact a pretty easy task until you find out he wants to send a 1.6Gb file. Still not a problem, you just have to adjust the php.ini file to allow for an upload of this size. Contrary to a lot that I've read, the only changes that need to be made are to the post_max_size and upload_max_filesize variables to allow for a file of this size. (I set them both to 10000M and that seemed to work fine). Quick test and done - works OK so I sent him a link. Next I get a note saying that there's no way to tell if it's working. In other words, you click submit and nothing happens until the file has completed the upload which, with a 1.6Gb file over a home network takes about 12 hours depending on the upload speed of the sender. After assuring him that it was in fact working, even though nothing appeared to be happening, he let it run and I got the file. Still, seemed like a good idea to figure out how to create some kind of progress bar.

This, I find is incredibly difficult. First, this appears to be something that has only recently been made available in PHP (v5.2). As best I understand, the latest version has some hook that you can get information from once an upload starts. Great - I'm on the latest version so this shouldn't be too difficult. PHP has these extensions (called PECL's) that you can install to give additional functionality. A quick look and there's a php_uploadprogress extension so surely you just install it and add the right syntax in your code and we're done. Well, best I can make out from the logs I've visited is that the person who developed the php_uploadprogress extension doesn't have a Windows machine, so it works great on *NIX systems, but the dll file doesn't actually work. It appears to have been fixed by someone else and is ready to be uploaded as a revised version to the PECL library, but doesn't appear to have been done yet. What's more, I found the revised dll file on another site <<<link removed as site no longer exists>>> at lunch, however, when I went to download the file at night, the site was down for 3 days. Thankfully, I finally got access and downloaded the dll and it worked.

Next thing is to code a php file to do all the calculating of the upload. I used 4 files to get this to work as follows:
- upload file form (where the file is submitted from)
- upload file progress (to process the upload once complete)
- upload progress (to display the progress of the upload)
- upload progress bar (used to create the dynamic picture of an increasing bar)

Starting at the beginning, obviously you need a form from which to submit the file. This is pretty easy. The form type must be of type "multipart/form-data" to send a file. The field to attach a file is an input field with a type of "file". You also need to create a hidden field before the file field called "UPLOAD_IDENTIFIER" in order to get the uploadprogress dll to work. You need to create a unique ID for the upload to work. I did this simply by using date+time+random. I'm not expecting a plethora of uploads such that this will not work.

Second step is to pop open a window from which to report the upload progress. Originally I tried to do this using the onclick event with the button which worked fine in Firefox, but not in Internet Explorer. The solution is to use the onsubmit event in the form as this seems to work in both browsers that I have. This window needs to refresh itself every x seconds to get updated information on the progress of the upload. I refresh every second, but you can figure out what works best for you. I used the onload event of the body tag to call the setTimeout function. This has a refresh time of 1 second (1000 milliseconds) and has the simple action to refresh (reload) the window or close it if the upload is complete. You can then use the uploadprogress_get_info() function to retrieve information about the progress of the file upload. Basically, installing the PECL and creating the UPLOAD_IDENTIFIER allows the system to tag upload information that can be fetched and put into an array with the uploadprogress_get_info() function. It provides the following:
- time_start - The time that the upload began.
- time_last - The time that the progress info was last updated.
- speed_average - Average speed.
- speed_last - Last measured speed.
- bytes_uploaded - Number of bytes uploaded so far.
- bytes_total - The value of the Content-Length header sent by the browser.
- files_uploaded - Number of files uploaded so far.
- est_sec - Estimated number of seconds remaining.


The graphic of the bar is created by calling a third file and passing it the variable of where the progress is. So, just calculate the % complete using the bytes_uploaded compared to bytes_total and pass it over. Nothing more complicated than that. (Actually, I did spend a lot of time trying to output a GD file in the upload_progress file, but couldn't figure out how to do it without saving the file and loading it which seemed excessive. If I've missed something here, it would be greatly appreciated). The graphic uses the GD capabilities in PHP so you will need to load the GD PECL. If you don't want to do that, you can just display text based progress.

Finally, the uploaded file is processed. In some places of this site, I do things with the file - normally to re-sample a graphic for display, so I reject files I can't handle.

The only thing missing from this description is that I use cookies (mmmmmm cookies) to store information as to where the upload is. Given that the upload progress disappears once the upload is complete, it is difficult to tell the difference of before the upload has started and once it is complete. As the file upload form is loaded, it creates a cookie with the unique ID that will be used for the file upload (in order to allow multiple uploads). It gives the cookie a value of 0. When the upload progress window is opened, it looks to see if the upload has started (if an info can be received from uploadprogress_get_info()). Once started, it gives the cookie a value of 1. The upload file process sets the cookie value to 2 when it loads as this is when the upload is complete. This is used to close the pop-up window instead of refreshing.

All the files I used including the working dll are in the attached <<<link removed as files are old>>> file. I haven't done anything with the dll file and am not trying to take credit for the work of others, that belongs to the original creator as noted on the PHP website or the person who updated it on the link provided earlier in this article. It just took me a long time to collect the bits, so I'm making them all available here. In addition to the notes above, these require you to have a directory on your root called "uploadedfiles". You also need a setting in your php.ini file to set the temporary directory for the uploadprogress extension to save files uploadprogress.file.filename_template = [root]:/tmp/%s.txt (note you need to include the %s as this is replaced with the file name of the upload - the unique identifier you created).

The files in the attached zip are the basic working files (they don't have any fancy headers or footers). If you want to have a go and see the upload progress in action, send me a file (just try and make it interesting). I've obviously added a lot of stuff to the form at the end of the link to match the house style of this site.

A couple of last notes. At times during a big upload, the upload file disappears, so you will jump to 100% complete and then back to whatever the correct % is (one more reason why I had to use cookies). Second, in MSIE, the bar flashes on and off each time it loads.

And finally, if you still have problems, let me know and I'll see if I can help.
3 comments
Rag
18:25:37
30th September 2011
OK - so, this code hasn't worked for a while. The problem has been that the dll is coded for a different version of php using an older compiler. I'm not actually sure what version it was setup with, but I'll leave it posted on the off chance it works for someone. My guess is it's PHP 5.2.x VC6.

Anyhoo, just got round to looking at it again and rather than using the php_uploadprogress extension, you can solve the problem using the php_apc extension. This extension moves php processing to memory to make the application run more efficiently, but one added side effect is that you can expose the RFCs. If you enable RFC 1867 (i.e. add “apc.rfc1867 = on” in the php.ini file after the extension has been loaded), you can set a field in your upload form with the name APC_UPLOAD_PROGRESS and can then use the apc_fetch function to find statistics about your upload (current bytes, total bytes etc.) by referencing “upload_” + the value you have in this field (which you'll want to make unique so you can find the statistics for a specific upload. Suppose you set the value of the APC_UPLOAD_PROGRESS field to 1234, you would set a variable to return the array of statistics with $status = apc_fectch('upload_1234');.

If you want an example of the files that will create this you can <<<link removed as files old>>> download them from this link. I didn't mention this above, but the solution I'm providing also requires the GD extension to be loaded as it uses this to draw the upload progress bar. This example pops open a little window that shows the progress of the upload.

If you want to see an example of this working, you can send me a file through the upload file form. Note that for this example I've embedded the upload progress bar as an iframe in the main form rather than calling a pop up window. If you want the example with the progress bar in the form, shoot me an email. Top tip if you try this yourself, have the iframe point to the upload_progress file and use the onchange event for the file field to change the filename associated with the iframe. If you leave the iframe source blank and then set in when you submit the form, the iframe doesn't refresh (at least it didn't on my system).
Rag
16:05:40
12th May 2022
Further update. I'm no longer using APC or APCu as I had issues with the compatibility after upgrading to PHP 7.4. I've created a new upload progress bar using AJAX and some javascript. It's pretty simple and you can see it in operation at upload file form.
Rag
18:22:24
7th January 2023
Removed links to downloads as the files are for very old versions of php now and I can't think that anyone still needs them

 

This is a Build Your Own Blog entry.