I have a PHP script that copies files from one folder to another in an S3 bucket using the copyObject
action. However, when a filename contains a percentage sign (%) this results in a 400 Bad Request error:
<Error><Code>InvalidArgument</Code><Message>Invalid copy source encoding</Message><ArgumentName>x-amz-copy-source</Argum (truncated...) InvalidArgument (client): Invalid copy source encoding - <Error><Code>InvalidArgument</Code><Message>Invalid copy source encoding</Message><ArgumentName>x-amz-copy-source</ArgumentName><ArgumentValue>x-amz-copy-source</ArgumentValue><RequestId>MJ6JPP4CJY87A4D2</RequestId><HostId>3iEO5xRKnpHhNAp+pTpGYUWgVEwP97k2qtQuJRTIiBKRL9KFGTMJLXUu8irha4PTtW2q3hWdxQA=</HostId></Error>'
An example of a problematic filename is: ID!@£$%.jpg
Relevant PHP code:-
$filename = 'ID!@£$%.png';
$file_key = 'source/' . $filename;
$new_file_key = 'destination/' . $filename;
$bucketname = 'my-bucket';
$result = $client->copyObject(array(
'Bucket' => $bucketname,
'Key' => $new_file_key,
'CopySource' => $bucketname."/".($file_key),
'MetadataDirective' => 'COPY',
));
I also tried making a copy-object operation with aws cli and it worked fine for this filename:
aws s3api copy-object --copy-source my-bucket/source/ID!@£$%.png --key destination/ID!@£$%.jpg --bucket my-bucket
Could someone please help me to figure out what I am doing wrong here?
Thank you.
>Solution :
When you are using AWS SDK for PHP to copy an object from one location to another in S3, the source (CopySource
) must be URL-encoded in a specific manner. The issue you’re facing with special characters, like %
, in the filename is due to this URL encoding requirement.
Here’s what you need to do:
-
URL-encode the filename:
Use PHP’srawurlencode()
function to encode the file key. This function is specifically designed for encoding query string values and is more suitable for this task thanurlencode()
. -
Avoid double encoding:
Since you’re already using URL encoding, ensure that the AWS SDK for PHP doesn’t attempt to encode the value again.
Let’s modify your code:
$filename = 'ID!@£$%.png';
$file_key = 'source/' . $filename;
$new_file_key = 'destination/' . $filename;
$bucketname = 'my-bucket';
// URL encode the file key for the copy source
$encoded_file_key = 'source/' . rawurlencode($filename);
$result = $client->copyObject(array(
'Bucket' => $bucketname,
'Key' => $new_file_key,
'CopySource' => $bucketname . "/" . $encoded_file_key,
'MetadataDirective' => 'COPY',
));
By URL encoding the file key for the CopySource
value, your code should work as expected for filenames that include special characters.