Search code examples
phpamazon-s3aws-php-sdk

How do I upload a file to S3 from GuzzleHttp\Psr7\Stream?


I'm using aws-php-sdk to save Outlook email attachments to S3. I'm able to upload the file to S3 but when I download the file and open it I only see the content byte data. I don't know how to store the content of the file using contentbytes. I've tried $attachment->getContentBytes()->getContents() but that always uploads a 0kb file and $attachment->getContentBytes() displays the content bytes when I open the file.

The FileAttachment is from msgraph-sdk-php which I'm using to get the Outlook email attachments.

private function saveToS3(string $s3_directory, \Microsoft\Graph\Model\FileAttachment $attachment)
{
  $aws_s3_key    = 'XXXXXXX';
  $aws_s3_secret = 'XXXXXXXX';
  $aws_s3_region = 'us-west-1';
  $aws_bucket    = 'test-storage';

  $s3Client = new S3Client([
      'version'     => 'latest',
      'region'      => $aws_s3_region,
      'credentials' => [
          'key'    => $aws_s3_key,
          'secret' => $aws_s3_secret,
      ],
  ]);

  return $s3Client->putObject([
      'Bucket'      => $aws_bucket,
      'Key'         => $s3_directory,
      'Body'        => $attachment->getContentBytes(),
      'ContentType' => $attachment->getContentType(),
  ]);
}

A sample FileAttachment:

{
  "@odata.type": "#microsoft.graph.fileAttachment",
  "@odata.mediaContentType": "application/octet-stream",
  "id": "AAMkAGQyMzYyNXXXXXXXXXXXXXXXxLjl14fH8FdpE=",
  "lastModifiedDateTime": "2023-07-31T22:43:52Z",
  "name": "README.md",
  "contentType": "application/octet-stream",
  "size": 1708,
  "isInline": false,
  "contentId": "f_leudosmi0",
  "contentLocation": null,
  "contentBytes": "IyBDUk0gaW5XXXXXXXXXXXXXXXXXXXXXgZnJhbWV3b3JrIAo="
}

How do I upload the file contents and not the content bytes to S3?


Solution

  • You can base64_decode the content bytes and send it to S3.

    return $s3Client->putObject([
        'Bucket'      => $aws_bucket,
        'Key'         => $s3_directory,
        'Body'        => base64_decode($attachment->getContentBytes()->getContents(), true),
        'ContentType' => $attachment->getContentType(),
    ]);
    

    Another solution could be, create and save the file from the content bytes.

    $content = base64_decode($attachment->getContentBytes()->getContents(), true);
    file_put_contents($attachment->getname(), $content);
    

    Then, you can pass the file to S3.

    return $s3Client->putObject([
        'Bucket'      => $aws_bucket,
        'Key'         => $s3_directory,
        'SourceFile'  => $attachment->getname(),
        'ContentType' => $attachment->getContentType(),
    ]);