upload_dir( $bucket, $bucket_dir, $local_dir ); class S3_Backup { function upload_dir( $bucket, $bucket_dir, $local_dir, $s3 = null ) { if( !$s3 ) { $s3 = new s3; $s3->putBucket( $bucket ); $s3->setBucketName( $bucket ); } $dp = opendir( $local_dir ); if( !$dp ) { echo "Fatal: Can't open local dir $local_dir\n"; exit; } while(( $file = readdir( $dp )) !== false ) { if( $file == '.' ) continue; if( $file == '..' ) continue; $local_file = $local_dir.'/'.$file; $local_file_type = filetype( $local_file ); $name = $bucket_dir.'/'.$file; // s3.class has this in the constructor and s3 wants it to // be fresher, so just update it here $s3->httpDate = gmdate(DATE_RFC822); // do not refresh unless hashes differ of course if( $local_file_type == 'file' ) { $etag = md5_file( $local_file ); $result = $s3->getObjectInfo( $name ); if( is_array( $result ) && $result['etag'] == '"'.$etag.'"' ) { echo "etag $etag matches for $name , skipping\n"; continue; } } // keep posix attributes lazily, stat should give us enough // to restore usefully $metadata = lstat( $local_file ); // oops, due to a sorting difference between s3.class and // amazon, let's just drop that numeric part of the stat() // array reset( $metadata ); while( list($key,$val) = each( $metadata )) { if( is_numeric( $key )) continue; $output_metadata[$key] = $val; } $metadata = $output_metadata; $metadata['ftype'] = $local_file_type; // archive file according to type switch( $local_file_type ) { case 'file': $data = file_get_contents( $local_file ); break; case 'link': $data = readlink( $local_file ); break; case 'dir': case 'fifo': case 'block': case 'char': default: $data = 'X'; break; } // make it amazon's problem $s3->putObject( $name, $data, NULL, NULL, NULL, $metadata ); echo "put: $name from ($local_file_type)".$local_file."\n"; // if it was a dir, recurse if( $local_file_type == 'dir' ) { $this->upload_dir( $bucket, $bucket_dir.'/'.$file, $local_dir.'/'.$file, $s3 ); continue; } } } }