66import mimetypes
77import boto3
88from botocore .exceptions import ClientError
9- import util
9+ from webotron import util
1010from hashlib import md5
1111from functools import reduce
1212
1313
1414class BucketManager :
1515 """Manage an S3 Bucket."""
16+
1617 CHUNK_SIZE = 8388608
1718
1819 def __init__ (self , session ):
@@ -104,24 +105,25 @@ def configure_website(self, bucket):
104105 )
105106
106107 def load_manifest (self , bucket ):
107- """Load manifest for caching purpose"""
108+ """Load manifest for caching purpose. """
108109 paginator = self .s3 .meta .client .get_paginator ('list_objects_v2' )
109110 for page in paginator .paginate (Bucket = bucket .name ):
110- for obj in page .get ('Contents' ,[]):
111+ for obj in page .get ('Contents' , []):
111112 self .manifest [obj ['Key' ]] = obj ['ETag' ]
113+
112114 @staticmethod
113115 def hash_data (data ):
114- """Generate md5 hash for data"""
115- hash = md5 ()
116+ """Generate md5 hash for data. """
117+ hash = md5 ()
116118 hash .update (data )
117119 return hash
118120
119121 def gen_etag (self , path ):
120- """Generate etag for file"""
122+ """Generate etag for file. """
121123 hashes = []
122- with open (path ,'rb' ) as f :
124+ with open (path , 'rb' ) as f :
123125 while True :
124- data = f .read (self .CHUNK_SIZE )
126+ data = f .read (self .CHUNK_SIZE )
125127 if not data :
126128 break
127129 hashes .append (self .hash_data (data ))
@@ -132,21 +134,19 @@ def gen_etag(self, path):
132134 else :
133135 hash = self .hash_data (
134136 reduce (
135- lambda x ,y : x + y ,
137+ lambda x , y : x + y ,
136138 (
137139 h .digest () for h in hashes
138140 )
139141 )
140142 )
141- return '"{}-{}"' .format (hash .hexdigest (),len (hashes ))
142-
143-
143+ return '"{}-{}"' .format (hash .hexdigest (), len (hashes ))
144144
145145 def upload_file (self , bucket , path , key ):
146146 """Upload path to S3 bucket with key."""
147147 content_type = mimetypes .guess_type (key )[0 ] or 'text/plain'
148148 etag = self .gen_etag (path )
149- if self .manifest .get (key ,'' ) == etag :
149+ if self .manifest .get (key , '' ) == etag :
150150 print ("Skipping {}, etag match" .format (key ))
151151 return
152152 return bucket .upload_file (
@@ -158,21 +158,56 @@ def upload_file(self, bucket, path, key):
158158 Config = self .transfer_config
159159 )
160160
161+ def delete_files (self , bucket , objects ):
162+ """Delete files from given bucket."""
163+ response = bucket .delete_objects (
164+ Delete = {
165+ 'Objects' : objects
166+ },
167+ RequestPayer = 'Webotron'
168+ )
169+ print (response )
170+ for item in response ['Deleted' ]:
171+ print (
172+ 'File has been successfully deleted : {}' .format (item ['Key' ])
173+ )
174+ if 'Errors' in response .keys ():
175+ for item in response ['Errors' ]:
176+ print (
177+ 'Error in deleteFile : {} ,'
178+ 'ErrorCode: {} , ErrorMsg: {}' .format (
179+ item ['Key' ], item ['Code' ], item ['Message' ]
180+ )
181+ )
182+
161183 def sync (self , pathname , bucket_name ):
162184 """Sync contents of path to bucket."""
163185 bucket = self .s3 .Bucket (bucket_name )
164186 root = Path (pathname ).expanduser ().resolve ()
165187 self .load_manifest (bucket )
188+ self .file_list_source = []
166189
167190 def handle_directory (target ):
168191 for p in target .iterdir ():
169192 if p .is_dir ():
170193 handle_directory (p )
171194 if p .is_file ():
195+ self .file_list_source .append (
196+ str (p .relative_to (root ).as_posix ())
197+ )
172198 self .upload_file (
173199 bucket ,
174200 str (p ),
175201 str (p .relative_to (root ).as_posix ())
176202 )
177203
204+ def handle_bucket (directory_flist , bucket_keylist ):
205+ files_delete = []
206+ for key in bucket_keylist .keys ():
207+ if key not in directory_flist :
208+ key_dic = {'Key' : key }
209+ files_delete .append (key_dic )
210+ self .delete_files (bucket , files_delete )
211+
178212 handle_directory (root )
213+ handle_bucket (self .file_list_source , self .manifest )
0 commit comments