upload-progress-metter.v2/0040755000000000000000000000000007672725431014547 5ustar rootrootupload-progress-metter.v2/README0100644000000000000000000000467107672725431015434 0ustar rootrootHi, To make a long story short, I needed an upload progress metter, so the users will something while their huge files are uploaded to the server. I searched the net but only found for ASP, so I wrote one. Unfortunatly PHP needs a little patch to be willing to do this ... Here is how it works: 1. index.php will generate and uniq ID for each upload. This ID will be used to track the progress and report it. 2. a special field named 'UPLOAD_METTER_ID' is used to store this ID. make sure this field is BEFORE any 'file' fields. put it at the begining of the form! 3. onSubmit()-ing the form a small window will open where the progress.php will display the actual progress bar. 4. php will check the value of 'upload_metter' and 'upload_metter_dir' configuration options and the presence and value of 'UPLOAD_METTER_ID' field 5. the progress file is stored in the directory named by 'upload_metter_dir' and the vlaue of UPLOAD_METTER_ID field is used as the name of the file 6. progress information is updated once a second by the php engine 7. script progress.php will use the ID field which it receives as a parameter to locate the associated progress-file and read progress information from it. then generate the little proggress bar 8. the progress bar will 'refresh' about once a second, depending on how fast the network is going 9. when upload is completed, the progress.php script will remove the progress file, and close the pop-up window. What the PATCH does: - adding 2 new configuration options to PHP: upload_metter and upload_metter_dir - in rfc1867.c: changes rfc1867_post_handler() to make some calls to a newly defined function that will update the progress metter: update_progress_metter() - in turn it calls update_progress_metter_file(), trying its best not to update the file more than once a second. - fix sapi/apache2handler and sapi/apache2filter that to not initialize SG(request_info).content_length needed to computer the progress metter. NOTE: there is a good chance that this progress files will not be deleted from various reasons (client don't have JS activated, or it closes the popup while data is still uploaded, etc...), so there is a need to periodically cleanup the directory of old files. INSTALATION: see INSTALL for a quick install guide. NOTE: Latest version and a live demo can be found here: http://pdoru.from.ro/ Author: Doru Theodor Petrescu Date: Version 1 was released on 2003-06-05 upload-progress-metter.v2/INSTALL0100644000000000000000000000155507672724773015613 0ustar rootroot Quick install: 1. apply patch to php and recompile php (and apache if needed) 2. add something like this to http.conf #for php-progress-bar php_value post_max_size 155000000 php_value upload_metter 1 php_value upload_metter_dir "/tmp/uploadbar" php_value upload_max_filesize 155000000 - This will activate the progress metter for /upload - And will tell it to write progress informations to /tmp/uploadbar 3. mkdir /tmp/uploadbar; chmod 777 /tmp/uploadbar I have to say that 0777 and /tmp are not the best choises from a security point of view. 4. copy the demo scripts to /upload directory 5. point your browser to the index.php script. upload some files. enjoy! NOTE: Latest version and a live demo can be found here: http://pdoru.from.ro/ Author: Doru Theodor Petrescu upload-progress-metter.v2/apache.config.add0100644000000000000000000000034107672725016017700 0ustar rootroot #for php-progress-bar php_value post_max_size 155000000 php_value upload_metter 1 php_value upload_metter_dir "/tmp/uploadbar" php_value upload_max_filesize 155000000 upload-progress-metter.v2/patch-php-4.3.2-upload-progress-metter.v2.txt0100644000000000000000000001665107672725052024613 0ustar rootrootdiff -rubB orig/php-4.3.2/main/main.c php-4.3.2/main/main.c --- orig/php-4.3.2/main/main.c Thu May 22 01:54:38 2003 +++ php-4.3.2/main/main.c Sun Jun 15 01:27:59 2003 @@ -345,7 +345,9 @@ STD_PHP_INI_BOOLEAN("file_uploads", "1", PHP_INI_SYSTEM, OnUpdateBool, file_uploads, php_core_globals, core_globals) STD_PHP_INI_ENTRY("upload_max_filesize", "2M", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateInt, upload_max_filesize, php_core_globals, core_globals) STD_PHP_INI_ENTRY("post_max_size", "8M", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateInt, post_max_size, sapi_globals_struct,sapi_globals) - STD_PHP_INI_ENTRY("upload_tmp_dir", NULL, PHP_INI_SYSTEM, OnUpdateStringUnempty, upload_tmp_dir, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("upload_tmp_dir", NULL, PHP_INI_ALL, OnUpdateStringUnempty, upload_tmp_dir, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("upload_metter", "0", PHP_INI_ALL, OnUpdateBool, upload_metter, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("upload_metter_dir", NULL, PHP_INI_ALL, OnUpdateStringUnempty, upload_metter_dir, php_core_globals, core_globals) STD_PHP_INI_ENTRY("user_dir", NULL, PHP_INI_SYSTEM, OnUpdateString, user_dir, php_core_globals, core_globals) STD_PHP_INI_ENTRY("variables_order", NULL, PHP_INI_ALL, OnUpdateStringUnempty, variables_order, php_core_globals, core_globals) diff -rubB orig/php-4.3.2/main/php_globals.h php-4.3.2/main/php_globals.h --- orig/php-4.3.2/main/php_globals.h Sun May 18 13:22:16 2003 +++ php-4.3.2/main/php_globals.h Sun Jun 15 01:27:59 2003 @@ -133,6 +133,8 @@ zend_bool modules_activated; zend_bool file_uploads; + zend_bool upload_metter; + char * upload_metter_dir; zend_bool during_request_startup; diff -rubB orig/php-4.3.2/main/rfc1867.c php-4.3.2/main/rfc1867.c --- orig/php-4.3.2/main/rfc1867.c Sat May 24 00:37:16 2003 +++ php-4.3.2/main/rfc1867.c Sun Jun 15 01:46:19 2003 @@ -676,6 +676,84 @@ return out; } +typedef struct _Xdata { + int time_start; + int time_last; + int speed_average; + int speed_last; + int bytes_uploaded; + int bytes_total; + int files_uploaded; + char progress[1024]; +} Xdata; + +static void update_progress_metter_file(Xdata *X) +{ + int eta,s; + FILE *F; + TSRMLS_FETCH(); + + F = VCWD_FOPEN(X->progress, "wb"); + + s = X->speed_average; if (s < 1) s=1; + eta = (X->bytes_total - X->bytes_uploaded) / s; + + if (F) { + fprintf(F, "time_start=%d\ntime_last=%d\nspeed_average=%d\nspeed_last=%d\nbytes_uploaded=%d\nbytes_total=%d\nfiles_uploaded=%d\neta=%d\n", + X->time_start, X->time_last, X->speed_average, X->speed_last, X->bytes_uploaded, X->bytes_total, X->files_uploaded, eta + ); + fclose(F); + } + +// sapi_module.sapi_error(E_NOTICE, "metter: read %d of %d", SG(read_post_bytes), SG(request_info).content_length ); +} + +static void update_progress_metter(Xdata *X, int read, int total) +{ + int d,dt,dtx; + int bu; + int sp; + + + if (!X->time_start) { + X->time_start = X->time_last = time(NULL); + + X->bytes_total = total; + X->bytes_uploaded = read; + + X->speed_average = X->speed_last = X->bytes_uploaded; + + update_progress_metter_file(X); + return; + } + + dt = time(NULL) - X->time_last; + d = read - X->bytes_uploaded; + + if (dt < 1) { + if (read < total) + return; // avoid divide by zero + if (d < 1) + return; + dt = 1; + } + + + + sp = d/dt; + + + X->bytes_uploaded = read; + X->time_last = time(NULL); + + dtx = X->time_last - X->time_start; if (dtx < 1) dtx = 1; + X->speed_average = X->bytes_uploaded / dtx; + + X->speed_last = sp; + + update_progress_metter_file(X); +} + /* * The combined READER/HANDLER @@ -693,6 +771,10 @@ zval *array_ptr = (zval *) arg; FILE *fp; zend_llist header; + Xdata X; + int progress_metter=0; + + bzero(&X,sizeof(X)); if (SG(request_info).content_length > SG(post_max_size)) { sapi_module.sapi_error(E_WARNING, "POST Content-Length of %d bytes exceeds the limit of %d bytes", SG(request_info).content_length, SG(post_max_size)); @@ -753,6 +835,9 @@ zend_llist_clean(&header); if (!multipart_buffer_headers(mbuff, &header TSRMLS_CC)) { + + + if (progress_metter) update_progress_metter( &X, SG(read_post_bytes), SG(request_info).content_length ); SAFE_RETURN; } @@ -806,6 +891,35 @@ max_file_size = atol(value); } + + + if (!strcmp(param, "UPLOAD_METTER_ID") && PG(upload_metter) && PG(upload_metter_dir)) { + char *c,*v = estrdup(value); + for (c=v;*c;c++) { + if ( (*c >= '0' && *c <= '9') || + (*c >= 'a' && *c <= 'z') || + (*c >= 'A' && *c <= 'Z') || + *c == '.' || *c == '_' || + *c == ',' || *c == '@' || + *c == '-' || *c == '%') { + }else{ + *c=0; + break; + } + } + + if (v && *v) { + if (strlen(v) > 64) v[64]=0; + progress_metter=1; + snprintf(X.progress,1000, "%s/%s", PG(upload_metter_dir), v); + } + efree(v); + + + + } + + efree(param); efree(value); continue; @@ -831,6 +945,9 @@ SAFE_RETURN; } + if (progress_metter) update_progress_metter( &X, SG(read_post_bytes), SG(request_info).content_length ); + X.files_uploaded++; + if (!skip_upload) { /* Handle file */ fp = php_open_temporary_file(PG(upload_tmp_dir), "php", &temp_filename TSRMLS_CC); @@ -872,6 +989,10 @@ } else { total_bytes += wlen; } + + + + if (progress_metter) update_progress_metter( &X, SG(read_post_bytes), SG(request_info).content_length ); } } fclose(fp); @@ -1047,6 +1168,10 @@ efree(param); } } + + + + if (progress_metter) update_progress_metter( &X, SG(read_post_bytes), SG(request_info).content_length ); SAFE_RETURN; } diff -rubB orig/php-4.3.2/sapi/apache2filter/sapi_apache2.c php-4.3.2/sapi/apache2filter/sapi_apache2.c --- orig/php-4.3.2/sapi/apache2filter/sapi_apache2.c Fri May 23 05:42:22 2003 +++ php-4.3.2/sapi/apache2filter/sapi_apache2.c Sun Jun 15 01:44:10 2003 @@ -385,6 +385,15 @@ SG(request_info).request_uri = safe_strdup(f->r->uri); SG(request_info).path_translated = safe_strdup(f->r->filename); f->r->no_local_copy = 1; + + // add this --- cut here --- + { + char *content_length = (char *) apr_table_get(r->headers_in, "Content-Length"); + SG(request_info).content_length = (content_length ? atoi(content_length) : 0); + } // --cut here -- + + + content_type = sapi_get_default_content_type(TSRMLS_C); f->r->content_type = apr_pstrdup(f->r->pool, content_type); SG(request_info).post_data = ctx->post_data; diff -rubB orig/php-4.3.2/sapi/apache2handler/sapi_apache2.c php-4.3.2/sapi/apache2handler/sapi_apache2.c --- orig/php-4.3.2/sapi/apache2handler/sapi_apache2.c Fri May 23 05:42:22 2003 +++ php-4.3.2/sapi/apache2handler/sapi_apache2.c Sun Jun 15 01:21:54 2003 @@ -420,6 +420,13 @@ SG(request_info).path_translated = apr_pstrdup(r->pool, r->filename); r->no_local_copy = 1; + // add this --- cut here --- + { + char *content_length = (char *) apr_table_get(r->headers_in, "Content-Length"); + SG(request_info).content_length = (content_length ? atoi(content_length) : 0); + } // --cut here -- + + content_type = sapi_get_default_content_type(TSRMLS_C); ap_set_content_type(r, apr_pstrdup(r->pool, content_type)); efree(content_type); upload-progress-metter.v2/patch-php-4.2.3-upload-progress-metter.v1.txt0100644000000000000000000001463707672725052024614 0ustar rootrootdiff -rubB orig/php-4.2.3/main/main.c php-4.2.3/main/main.c --- orig/php-4.2.3/main/main.c Thu Jul 25 12:35:18 2002 +++ php-4.2.3/main/main.c Thu Jun 5 01:42:06 2003 @@ -261,8 +261,10 @@ STD_PHP_INI_ENTRY("safe_mode_exec_dir", "1", PHP_INI_SYSTEM, OnUpdateString, safe_mode_exec_dir, php_core_globals, core_globals) STD_PHP_INI_ENTRY("upload_max_filesize", "2M", PHP_INI_ALL, OnUpdateInt, upload_max_filesize, php_core_globals, core_globals) STD_PHP_INI_ENTRY("file_uploads", "1", PHP_INI_ALL, OnUpdateBool, file_uploads, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("upload_metter", "0", PHP_INI_ALL, OnUpdateBool, upload_metter, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("upload_metter_dir", NULL, PHP_INI_ALL, OnUpdateStringUnempty, upload_metter_dir, php_core_globals, core_globals) STD_PHP_INI_ENTRY("post_max_size", "8M", PHP_INI_SYSTEM, OnUpdateInt, post_max_size, sapi_globals_struct,sapi_globals) - STD_PHP_INI_ENTRY("upload_tmp_dir", NULL, PHP_INI_SYSTEM, OnUpdateStringUnempty, upload_tmp_dir, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("upload_tmp_dir", NULL, PHP_INI_ALL, OnUpdateStringUnempty, upload_tmp_dir, php_core_globals, core_globals) STD_PHP_INI_ENTRY("user_dir", NULL, PHP_INI_SYSTEM, OnUpdateStringUnempty, user_dir, php_core_globals, core_globals) STD_PHP_INI_ENTRY("variables_order", NULL, PHP_INI_ALL, OnUpdateStringUnempty, variables_order, php_core_globals, core_globals) diff -rubB orig/php-4.2.3/main/php_globals.h php-4.2.3/main/php_globals.h --- orig/php-4.2.3/main/php_globals.h Thu Feb 28 10:27:03 2002 +++ php-4.2.3/main/php_globals.h Thu Jun 5 01:24:52 2003 @@ -125,6 +126,8 @@ zend_bool modules_activated; zend_bool file_uploads; + zend_bool upload_metter; + char * upload_metter_dir; zend_bool during_request_startup; diff -rubB orig/php-4.2.3/main/rfc1867.c php-4.2.3/main/rfc1867.c --- orig/php-4.2.3/main/rfc1867.c Sat Aug 17 15:01:28 2002 +++ php-4.2.3/main/rfc1867.c Thu Jun 5 01:36:43 2003 @@ -181,6 +181,8 @@ char *buf = self->buffer + self->bytes_in_buffer; actual_read = sapi_module.read_post(buf, bytes_to_read TSRMLS_CC); + + /* update the buffer length */ if (actual_read > 0) { @@ -600,6 +602,81 @@ return out; } +typedef struct _Xdata { + int time_start; + int time_last; + int speed_average; + int speed_last; + int bytes_uploaded; + int bytes_total; + int files_uploaded; + char progress[1024]; +} Xdata; + +static void update_progress_metter_file(Xdata *X) +{ + int eta,s; + FILE *F = VCWD_FOPEN(X->progress, "wb"); + + s = X->speed_average; if (s < 1) s=1; + eta = (X->bytes_total - X->bytes_uploaded) / s; + + if (F) { + fprintf(F, "time_start=%d\ntime_last=%d\nspeed_average=%d\nspeed_last=%d\nbytes_uploaded=%d\nbytes_total=%d\nfiles_uploaded=%d\neta=%d\n", + X->time_start, X->time_last, X->speed_average, X->speed_last, X->bytes_uploaded, X->bytes_total, X->files_uploaded, eta + ); + fclose(F); + } + + sapi_module.sapi_error(E_NOTICE, "metter: read %d of %d", SG(read_post_bytes), SG(request_info).content_length ); +} + +static void update_progress_metter(Xdata *X, int read, int total) +{ + int d,dt,dtx; + int bu; + int sp; + + + if (!X->time_start) { + X->time_start = X->time_last = time(NULL); + + X->bytes_total = total; + X->bytes_uploaded = read; + + X->speed_average = X->speed_last = X->bytes_uploaded; + + update_progress_metter_file(X); + return; + } + + dt = time(NULL) - X->time_last; + d = read - X->bytes_uploaded; + + if (dt < 1) { + if (read < total) + return; // avoid divide by zero + if (d < 1) + return; + dt = 1; + } + + + + sp = d/dt; + + + X->bytes_uploaded = read; + X->time_last = time(NULL); + + dtx = X->time_last - X->time_start; if (dtx < 1) dtx = 1; + X->speed_average = X->bytes_uploaded / dtx; + + X->speed_last = sp; + + update_progress_metter_file(X); +} + /* * The combined READER/HANDLER @@ -617,12 +694,20 @@ zval *array_ptr = (zval *) arg; FILE *fp; zend_llist header; + Xdata X; + int progress_metter=0; + + bzero(&X,sizeof(X)); if (SG(request_info).content_length > SG(post_max_size)) { sapi_module.sapi_error(E_WARNING, "POST Content-Length of %d bytes exceeds the limit of %d bytes", SG(request_info).content_length, SG(post_max_size)); return; } + + + + /* Get the boundary */ boundary = strstr(content_type_dup, "boundary"); if (!boundary || !(boundary=strchr(boundary, '='))) { @@ -674,8 +759,11 @@ int blen=0, wlen=0; zend_llist_clean(&header); - + if (!multipart_buffer_headers(mbuff, &header TSRMLS_CC)) { + + + if (progress_metter) update_progress_metter( &X, SG(read_post_bytes), SG(request_info).content_length ); SAFE_RETURN; } @@ -729,6 +816,35 @@ max_file_size = atol(value); } + + + if (!strcmp(param, "UPLOAD_METTER_ID") && PG(upload_metter) && PG(upload_metter_dir)) { + char *c,*v = estrdup(value); + for (c=v;*c;c++) { + if ( (*c >= '0' && *c <= '9') || + (*c >= 'a' && *c <= 'z') || + (*c >= 'A' && *c <= 'Z') || + *c == '.' || *c == '_' || + *c == ',' || *c == '@' || + *c == '-' || *c == '%') { + }else{ + *c=0; + break; + } + } + + if (v && *v) { + if (strlen(v) > 64) v[64]=0; + progress_metter=1; + snprintf(X.progress,1000, "%s/%s", PG(upload_metter_dir), v); + } + efree(v); + + + + } + + efree(param); efree(value); continue; @@ -754,6 +870,12 @@ SAFE_RETURN; } + + + if (progress_metter) update_progress_metter( &X, SG(read_post_bytes), SG(request_info).content_length ); + + X.files_uploaded++; + /* Handle file */ fp = php_open_temporary_file(PG(upload_tmp_dir), "php", &temp_filename TSRMLS_CC); if (!fp) { @@ -788,9 +910,15 @@ } else { total_bytes += wlen; } + + + + if (progress_metter) update_progress_metter( &X, SG(read_post_bytes), SG(request_info).content_length ); } } fclose(fp); + + #ifdef DEBUG_FILE_UPLOAD if(strlen(filename) > 0 && total_bytes == 0) { @@ -956,6 +1084,10 @@ efree(param); } } + + + + if (progress_metter) update_progress_metter( &X, SG(read_post_bytes), SG(request_info).content_length ); SAFE_RETURN; } upload-progress-metter.v2/demo_scripts/0040755000000000000000000000000007672725535017247 5ustar rootrootupload-progress-metter.v2/demo_scripts/index.php0100644000000000000000000000204507672725535021065 0ustar rootroot Upload test generated new UPLOAD_METTER_ID =
php-config.upload_metter =
php-config.upload_metter_dir =

Select File 1:
Select File 2:
Select File 3:
Select File 4:
Select File 5:


Download upload-progress-metter.v2.tgz upload-progress-metter.v2/demo_scripts/progress.php0100644000000000000000000000615507672725230021620 0ustar rootroot 0, "time_last" => 0, "speed_average" => 0, "speed_last" => 0, "bytes_uploaded" => 0, "bytes_total" => 1, "files_uploaded" => 0, "eta" => 0 ); $file = get_progress_metter_file(); if (!$file) return FALSE; $F = @fopen($file, "r"); if (!$F) return $X; while (!feof ($F)) { $buffer = fgets($F, 100); if (preg_match('/(\w+)=(\d+)/', $buffer, $matches)) { $X[$matches[1]]=$matches[2]; } } fclose($F); return $X; } function remove_progress_metter_file() { $file = get_progress_metter_file(); if (!$file) return; unlink($file); } function get_progress_metter_file() { $id = $_GET["UPLOAD_METTER_ID"]; $dir = ini_get('upload_metter_dir'); if (!isset($id)) return FALSE; if (!isset($dir)) return FALSE; if (!$dir) return FALSE; if (!$id) return FALSE; // if (preg_match('/[\'\"\`\/\\\?\*\&\^]|\.\./', $id)) { ## cannot contain .. or '"`?*&^ or /-es or \-es if (preg_match('/[\'\"\`]|\.\./', $id)) { ## cannot contain .. or '"`?*&^ or /-es or \-es return ""; }else{ return "$dir/$id"; } } function nice_value($x) { if ($x < 100) $x; if ($x < 10000) return sprintf("%.2fKB", $x/1000); if ($x < 900000) return sprintf("%dKB", $x/1000); return sprintf("%.2fMB", $x/1000/1000); } $X = read_progress_metter(); if (!$X) { echo (' Invalid metter ID!'); }else{ $metter = sprintf("%.2f", $X['bytes_uploaded'] / $X['bytes_total'] * 100); $sp = $X['speed_last']; if ($sp < 10000) $speed = sprintf("%.2f", $sp / 1000); else $speed = sprintf("%d", $sp / 1000); $eta = sprintf("%02d:%02d", $X['eta'] / 60, $X['eta'] % 60 ); $upl = nice_value($X['bytes_uploaded']); $total = nice_value($X['bytes_total']); if ($X['bytes_total'] > 1 && $X['bytes_uploaded'] >= $X['bytes_total'] && $X['eta'] == 0) { echo (' UPLOAD completed!'); remove_progress_metter_file(); }else{ ?> Uploading Files... Please wait ... Uploading files...
 \n"); } ?>
left (at KB/sec) /(%)
upload-progress-metter.v2/demo_scripts/upload_done.php0100644000000000000000000000014207672725111022231 0ustar rootroot

Upload completed ...

<<BACK to upload some more ....