diff --git a/ora/amazon_aws_s3_pkg.pkb b/ora/amazon_aws_s3_pkg.pkb index 940a37f..c4af43b 100755 --- a/ora/amazon_aws_s3_pkg.pkb +++ b/ora/amazon_aws_s3_pkg.pkb @@ -543,7 +543,7 @@ as l_xml xmltype; l_xml_is_truncated xmltype; l_xml_next_continuation xmltype; - + l_date_str varchar2(255); l_auth_str varchar2(255); @@ -559,9 +559,9 @@ begin Purpose: get objects Remarks: see http://docs.aws.amazon.com/AmazonS3/latest/API/v2-RESTBucketGET.html - + see http://code.google.com/p/plsql-utils/issues/detail?id=16 - + "I've rewritten get_object_list as an internal procedure that uses the "marker" parameter, so that get_object_tab can now call the Amazon API multiple times to return the complete set of objects. The get_object_list function remains functionally unchanged in this version - it just returns one set of objects - @@ -577,7 +577,7 @@ begin KJS 06.10.2016 Modified to use newest S3 API which performs much better on large buckets. Changed for-loop to bulk operation. */ - + l_date_str := amazon_aws_auth_pkg.get_date_string; l_auth_str := amazon_aws_auth_pkg.get_auth_string ('GET' || chr(10) || chr(10) || chr(10) || l_date_str || chr(10) || '/' || p_bucket_name || '/'); @@ -597,7 +597,7 @@ begin l_header_values(3) := l_auth_str; if p_next_continuation_token is not null then - l_clob := make_request (get_url(p_bucket_name) || '?list-type=2&continuation-token=' || utl_url.escape(p_next_continuation_token) || '&max-keys=' || p_max_keys || '&prefix=' || utl_url.escape(p_prefix), 'GET', l_header_names, l_header_values, null); + l_clob := make_request (get_url(p_bucket_name) || '?list-type=2&max-keys=' || p_max_keys || '&prefix=' || utl_url.escape(p_prefix) || '&continuation-token=' || utl_url.escape(p_next_continuation_token, true), 'GET', l_header_names, l_header_values, null); else l_clob := make_request (get_url(p_bucket_name) || '?list-type=2&max-keys=' || p_max_keys || '&prefix=' || utl_url.escape(p_prefix), 'GET', l_header_names, l_header_values, null); end if; @@ -612,10 +612,10 @@ begin to_date(extractValue(value(t), '*/LastModified', g_aws_namespace_s3_full), g_date_format_xml) bulk collect into l_returnvalue from table(xmlsequence(l_xml.extract('//ListBucketResult/Contents', g_aws_namespace_s3_full))) t; - + -- check if this is the last set of data or not, and set the in/out p_next_continuation_token as expected l_xml_is_truncated := l_xml.extract('//ListBucketResult/IsTruncated/text()', g_aws_namespace_s3_full); - + if l_xml_is_truncated is not null and l_xml_is_truncated.getStringVal = 'true' then l_xml_next_continuation := l_xml.extract('//ListBucketResult/NextContinuationToken/text()', g_aws_namespace_s3_full); if l_xml_next_continuation is not null then @@ -876,6 +876,84 @@ begin end delete_object; +procedure copy_object (p_src_bucket_name in varchar2, + p_src_key in varchar2, + p_dest_bucket_name in varchar2, + p_dest_key in varchar2, + p_acl in varchar2 := null) is + l_src_key varchar2(4000) := utl_url.escape (p_src_key); + l_dest_key varchar2(4000) := utl_url.escape (p_dest_key); + + l_clob clob; + + l_date_str varchar2(255); + l_auth_str varchar2(255); + + l_header_names t_str_array := t_str_array(); + l_header_values t_str_array := t_str_array(); + +begin + + /* + + Purpose: copy existing object to new destination + + Remarks: see http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectCOPY.html + + Who Date Description + ------ ---------- ------------------------------------- + KJS 24.01.2017 Created + + */ + + l_date_str := amazon_aws_auth_pkg.get_date_string; + + if p_acl is not null then + l_auth_str := amazon_aws_auth_pkg.get_auth_string ('PUT' || chr(10) || chr(10) || chr(10) || + l_date_str || chr(10) || + 'x-amz-acl:' || p_acl || chr(10) || + 'x-amz-copy-source:' || '/' || p_src_bucket_name || '/' || l_src_key || chr(10) || + '/' || p_dest_bucket_name || '/' || l_dest_key); + else + l_auth_str := amazon_aws_auth_pkg.get_auth_string ('PUT' || chr(10) || chr(10) || chr(10) || + l_date_str || chr(10) || + 'x-amz-copy-source:' || '/' || p_src_bucket_name || '/' || l_src_key || chr(10) || + '/' || p_dest_bucket_name || '/' || l_dest_key); + end if; + + l_header_names.extend; + l_header_names(1) := 'Host'; + l_header_values.extend; + l_header_values(1) := get_host(p_dest_bucket_name); + + l_header_names.extend; + l_header_names(2) := 'Date'; + l_header_values.extend; + l_header_values(2) := l_date_str; + + l_header_names.extend; + l_header_names(3) := 'Authorization'; + l_header_values.extend; + l_header_values(3) := l_auth_str; + + l_header_names.extend; + l_header_names(4) := 'x-amz-copy-source'; + l_header_values.extend; + l_header_values(4) := '/' || p_src_bucket_name || '/' || l_src_key; + + -- Per documentation, default ACL is always private to the authorized user, that is, this API does not copy the existing object ACL + if p_acl is not null then + l_header_names.extend; + l_header_names(5) := 'x-amz-acl'; + l_header_values.extend; + l_header_values(5) := p_acl; + end if; + + l_clob := make_request (get_url (p_src_bucket_name, l_dest_key), 'PUT', l_header_names, l_header_values); + + check_for_errors (l_clob); +end copy_object; + function get_object (p_bucket_name in varchar2, p_key in varchar2) return blob @@ -1203,4 +1281,3 @@ end set_object_acl; end amazon_aws_s3_pkg; / -