git-svn-id: https://s3tools.svn.sourceforge.net/svnroot/s3tools/s3cmd/trunk@337 830e0280-6d2a-0410-9c65-932aecc39d9d
Michal Ludvig authored on 2009/01/15 19:51:30... | ... |
@@ -3,6 +3,7 @@ |
3 | 3 |
## http://www.logix.cz/michal |
4 | 4 |
## License: GPL Version 2 |
5 | 5 |
|
6 |
+import os |
|
6 | 7 |
import re |
7 | 8 |
import sys |
8 | 9 |
from BidirMap import BidirMap |
... | ... |
@@ -117,6 +118,12 @@ class S3UriFile(S3Uri): |
117 | 117 |
def uri(self): |
118 | 118 |
return "/".join(["file:/", self.path()]) |
119 | 119 |
|
120 |
+ def isdir(self): |
|
121 |
+ return os.path.isdir(self.path()) |
|
122 |
+ |
|
123 |
+ def dirname(self): |
|
124 |
+ return os.path.dirname(self.path()) |
|
125 |
+ |
|
120 | 126 |
if __name__ == "__main__": |
121 | 127 |
uri = S3Uri("s3://bucket/object") |
122 | 128 |
print "type() =", type(uri) |
... | ... |
@@ -169,6 +169,29 @@ def cmd_bucket_delete(args): |
169 | 169 |
_bucket_delete_one(uri) |
170 | 170 |
output(u"Bucket '%s' removed" % uri.uri()) |
171 | 171 |
|
172 |
+def fetch_local_list(args): |
|
173 |
+ local_uris = [] |
|
174 |
+ local_list = {} |
|
175 |
+ |
|
176 |
+ for arg in args: |
|
177 |
+ uri = S3Uri(arg) |
|
178 |
+ if not uri.type == 'file': |
|
179 |
+ raise ParameterError("Expecting filename or directory instead of: %s" % arg) |
|
180 |
+ if uri.isdir() and not cfg.recursive: |
|
181 |
+ raise ParameterError("Use --recursive to upload a directory: %s" % arg) |
|
182 |
+ local_uris.append(uri) |
|
183 |
+ |
|
184 |
+ for uri in local_uris: |
|
185 |
+ filelist = _get_filelist_local(uri) |
|
186 |
+ for key in filelist: |
|
187 |
+ upload_item = { |
|
188 |
+ 'file_info' : filelist[key], |
|
189 |
+ 'relative_key' : key, |
|
190 |
+ } |
|
191 |
+ local_list[key] = filelist[key] |
|
192 |
+ |
|
193 |
+ return local_list |
|
194 |
+ |
|
172 | 195 |
def fetch_remote_list(args): |
173 | 196 |
remote_uris = [] |
174 | 197 |
remote_list = [] |
... | ... |
@@ -229,39 +252,48 @@ def fetch_remote_list(args): |
229 | 229 |
return remote_list |
230 | 230 |
|
231 | 231 |
def cmd_object_put(args): |
232 |
- s3 = S3(Config()) |
|
232 |
+ cfg = Config() |
|
233 |
+ s3 = S3(cfg) |
|
234 |
+ |
|
235 |
+ ## Each item will be a dict with the following attributes |
|
236 |
+ # {'remote_uri', 'local_filename'} |
|
237 |
+ upload_list = [] |
|
233 | 238 |
|
234 |
- uri_arg = args.pop() |
|
235 |
- check_args_type(args, 'file', 'filename') |
|
239 |
+ if len(args) == 0: |
|
240 |
+ raise ParameterError("Nothing to upload. Expecting a local file or directory.") |
|
236 | 241 |
|
237 |
- uri = S3Uri(uri_arg) |
|
238 |
- if uri.type != "s3": |
|
239 |
- raise ParameterError("Expecting S3 URI instead of '%s'" % uri_arg) |
|
242 |
+ dst_uri = S3Uri(args.pop()) |
|
243 |
+ if dst_uri.type != 's3': |
|
244 |
+ raise ParameterError("Destination must be S3Uri. Got: %s" % args[-1]) |
|
240 | 245 |
|
241 |
- if len(args) > 1 and uri.object() != "" and not Config().force: |
|
242 |
- error(u"When uploading multiple files the last argument must") |
|
243 |
- error(u"be a S3 URI specifying just the bucket name") |
|
244 |
- error(u"WITHOUT object name!") |
|
245 |
- error(u"Alternatively use --force argument and the specified") |
|
246 |
- error(u"object name will be prefixed to all stored filenames.") |
|
247 |
- sys.exit(1) |
|
248 |
- |
|
246 |
+ if len(args) == 0: |
|
247 |
+ raise ParameterError("Nothing to upload. Expecting a local file or directory.") |
|
248 |
+ |
|
249 |
+ local_list = fetch_local_list(args) |
|
250 |
+ local_count = len(local_list) |
|
251 |
+ |
|
252 |
+ if local_count > 1 and not dst_uri.object() == "" and not dst_uri.object().endswith("/"): |
|
253 |
+ raise ParameterError(u"When uploading multiple files the last argument must be a S3 URI ending with '/' or a bucket name only!") |
|
254 |
+ |
|
255 |
+ sorted_local_keys = local_list.keys() |
|
256 |
+ sorted_local_keys.sort() |
|
249 | 257 |
seq = 0 |
250 |
- total = len(args) |
|
251 |
- for file in args: |
|
258 |
+ for key in sorted_local_keys: |
|
252 | 259 |
seq += 1 |
253 |
- uri_arg_final = str(uri) |
|
254 |
- if len(args) > 1 or uri.object() == "": |
|
255 |
- uri_arg_final += os.path.basename(file) |
|
256 |
- |
|
257 |
- uri_final = S3Uri(uri_arg_final) |
|
260 |
+ |
|
261 |
+ if local_count > 1 or dst_uri.object() == "": |
|
262 |
+ uri_final = S3Uri(u"%s%s" % (dst_uri, key)) |
|
263 |
+ else: |
|
264 |
+ uri_final = dst_uri |
|
265 |
+ |
|
258 | 266 |
extra_headers = {} |
259 |
- real_filename = file |
|
260 |
- seq_label = "[%d of %d]" % (seq, total) |
|
267 |
+ full_name_orig = local_list[key]['full_name'] |
|
268 |
+ full_name = full_name_orig |
|
269 |
+ seq_label = "[%d of %d]" % (seq, local_count) |
|
261 | 270 |
if Config().encrypt: |
262 |
- exitcode, real_filename, extra_headers["x-amz-meta-s3tools-gpgenc"] = gpg_encrypt(file) |
|
271 |
+ exitcode, full_name, extra_headers["x-amz-meta-s3tools-gpgenc"] = gpg_encrypt(full_name_orig) |
|
263 | 272 |
try: |
264 |
- response = s3.object_put(real_filename, uri_final, extra_headers, extra_label = seq_label) |
|
273 |
+ response = s3.object_put(full_name, uri_final, extra_headers, extra_label = seq_label) |
|
265 | 274 |
except S3UploadError, e: |
266 | 275 |
error(u"Upload of '%s' failed too many times. Skipping that file." % real_filename) |
267 | 276 |
continue |
... | ... |
@@ -271,14 +303,14 @@ def cmd_object_put(args): |
271 | 271 |
speed_fmt = formatSize(response["speed"], human_readable = True, floating_point = True) |
272 | 272 |
if not Config().progress_meter: |
273 | 273 |
output(u"File '%s' stored as %s (%d bytes in %0.1f seconds, %0.2f %sB/s) %s" % |
274 |
- (file, uri_final, response["size"], response["elapsed"], speed_fmt[0], speed_fmt[1], |
|
275 |
- seq_label)) |
|
274 |
+ (unicodise(full_name_orig), uri_final, response["size"], response["elapsed"], |
|
275 |
+ speed_fmt[0], speed_fmt[1], seq_label)) |
|
276 | 276 |
if Config().acl_public: |
277 | 277 |
output(u"Public URL of the object is: %s" % |
278 | 278 |
(uri_final.public_url())) |
279 |
- if Config().encrypt and real_filename != file: |
|
280 |
- debug(u"Removing temporary encrypted file: %s" % real_filename) |
|
281 |
- os.remove(real_filename) |
|
279 |
+ if Config().encrypt and full_name != full_name_orig: |
|
280 |
+ debug(u"Removing temporary encrypted file: %s" % unicodise(full_name)) |
|
281 |
+ os.remove(full_name) |
|
282 | 282 |
|
283 | 283 |
def cmd_object_get(args): |
284 | 284 |
cfg = Config() |
... | ... |
@@ -487,29 +519,31 @@ def cmd_info(args): |
487 | 487 |
|
488 | 488 |
def _get_filelist_local(local_uri): |
489 | 489 |
info(u"Compiling list of local files...") |
490 |
- local_path = deunicodise(local_uri.path()) |
|
491 |
- if os.path.isdir(local_path): |
|
492 |
- loc_base = os.path.join(local_path, "") |
|
490 |
+ if local_uri.isdir(): |
|
491 |
+ local_base = local_uri.basename() |
|
492 |
+ local_path = deunicodise(local_uri.path()) |
|
493 | 493 |
filelist = os.walk(local_path) |
494 | 494 |
else: |
495 |
- loc_base = "." + os.path.sep |
|
496 |
- filelist = [( '.', [], [local_path] )] |
|
497 |
- loc_base_len = len(loc_base) |
|
495 |
+ local_base = "" |
|
496 |
+ local_path = deunicodise(local_uri.dirname()) |
|
497 |
+ filelist = [( local_path, [], [deunicodise(local_uri.basename())] )] |
|
498 | 498 |
loc_list = {} |
499 | 499 |
for root, dirs, files in filelist: |
500 |
+ rel_root = root.replace(local_path, local_base, 1) |
|
500 | 501 |
## TODO: implement explicit exclude |
501 | 502 |
for f in files: |
502 | 503 |
full_name = os.path.join(root, f) |
504 |
+ rel_name = os.path.join(rel_root, f) |
|
503 | 505 |
if not os.path.isfile(full_name): |
504 | 506 |
continue |
505 | 507 |
if os.path.islink(full_name): |
506 | 508 |
## Synchronize symlinks... one day |
507 | 509 |
## for now skip over |
508 | 510 |
continue |
509 |
- file = unicodise(full_name[loc_base_len:]) |
|
511 |
+ relative_file = unicodise(rel_name) |
|
510 | 512 |
sr = os.stat_result(os.lstat(full_name)) |
511 |
- loc_list[file] = { |
|
512 |
- 'full_name_unicoded' : unicodise(full_name), |
|
513 |
+ loc_list[relative_file] = { |
|
514 |
+ 'full_name_unicode' : unicodise(full_name), |
|
513 | 515 |
'full_name' : full_name, |
514 | 516 |
'size' : sr.st_size, |
515 | 517 |
'mtime' : sr.st_mtime, |