{"id":18846,"date":"2024-08-06T17:56:52","date_gmt":"2024-08-06T12:26:52","guid":{"rendered":"https:\/\/opstree.com\/blog\/?p=18846"},"modified":"2026-03-05T18:03:14","modified_gmt":"2026-03-05T12:33:14","slug":"uploading-files-using-pre-signed-urls-to-a-specific-storage-class","status":"publish","type":"post","link":"https:\/\/opstree.com\/blog\/2024\/08\/06\/uploading-files-using-pre-signed-urls-to-a-specific-storage-class\/","title":{"rendered":"Uploading Files Using Pre-Signed URLs to a Specific Storage Class"},"content":{"rendered":"<p>Pre-signed URLs are unique web links designed to provide temporary access to a private resource, such as an Amazon S3 object, without requiring the user to provide their own security credentials.<\/p>\r\n<p>This approach allows you to generate a URL that includes authentication details and permissions, so users or applications can upload files directly to cloud storage without needing to have credentials for the storage service.<\/p>\r\n<p>Here\u2019s a step-by-step guide on how to implement file uploads using pre-signed URLs to a specific storage class, specifically with <a href=\"https:\/\/opstree.com\/blog\/2026\/02\/17\/secure-website-hosting-aws-s3-cloudfront-oac\/\">AWS S3<\/a>. I&#8217;ll cover how to generate a pre-signed URL in Python and how to use it in Postman.<\/p>\r\n<p><!--more--><\/p>\r\n<h2><b>Architecture:<\/b><\/h2>\r\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-18857 size-full\" src=\"https:\/\/opstree.com\/blog\/wp-content\/uploads\/2024\/08\/presign_url.png\" alt=\"\" width=\"1863\" height=\"733\" srcset=\"https:\/\/opstree.com\/blog\/wp-content\/uploads\/2024\/08\/presign_url.png 1863w, https:\/\/opstree.com\/blog\/wp-content\/uploads\/2024\/08\/presign_url-300x118.png 300w, https:\/\/opstree.com\/blog\/wp-content\/uploads\/2024\/08\/presign_url-1024x403.png 1024w, https:\/\/opstree.com\/blog\/wp-content\/uploads\/2024\/08\/presign_url-768x302.png 768w, https:\/\/opstree.com\/blog\/wp-content\/uploads\/2024\/08\/presign_url-1536x604.png 1536w, https:\/\/opstree.com\/blog\/wp-content\/uploads\/2024\/08\/presign_url-1200x472.png 1200w\" sizes=\"(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><\/p>\r\n<h3><b>Create an IAM User:<\/b><\/h3>\r\n<ul>\r\n<li aria-level=\"1\">Sign in to the AWS Management Console.<\/li>\r\n<li aria-level=\"1\">Navigate to IAM (Identity and Access Management):<\/li>\r\n<li aria-level=\"2\">Open the <a href=\"https:\/\/console.aws.amazon.com\/iam\/home\" rel=\"nofollow noopener\" target=\"_blank\">IAM Console<\/a>.<\/li>\r\n<li><b>Create a New User:<\/b><\/li>\r\n<li aria-level=\"1\">Click on <b>Users<\/b> in the sidebar.<\/li>\r\n<li aria-level=\"1\">Click the <b>Add user<\/b> button.<\/li>\r\n<li aria-level=\"1\">Enter a user name (e.g., s3-uploader).<\/li>\r\n<li aria-level=\"1\">Select <b>Programmatic access<\/b> for the access type to generate an access key ID and secret access key.<\/li>\r\n<li aria-level=\"1\">Click <b>Next: Permissions<\/b>.<\/li>\r\n<\/ul>\r\n<h3><b>Create an S3 Bucket:<\/b><\/h3>\r\n<ul>\r\n<li aria-level=\"1\"><b>Navigate to S3:<\/b><\/li>\r\n<li aria-level=\"2\">Open the<a href=\"https:\/\/console.aws.amazon.com\/s3\/home\" target=\"_blank\" rel=\"noopener\"> S3 Console<\/a>.<\/li>\r\n<li aria-level=\"2\"><b>Create a New Bucket<\/b>:<\/li>\r\n<li aria-level=\"2\">Click on <b>Create bucket<\/b>.<\/li>\r\n<li aria-level=\"2\">Enter a unique bucket name (e.g., data-from-resign).<\/li>\r\n<li aria-level=\"2\">Choose a region.<\/li>\r\n<li aria-level=\"2\">Configure options as needed (default settings are usually sufficient for this example).<\/li>\r\n<li aria-level=\"2\">Click <b>Create bucket<\/b>.<\/li>\r\n<\/ul>\r\n<h3><b>Edit Cross-origin resource sharing (CORS):<\/b><\/h3>\r\n<ul>\r\n<li aria-level=\"1\">Under the Permissions tab, find the CORS configuration section.<\/li>\r\n<li aria-level=\"1\">Click on Edit to modify the CORS settings.<\/li>\r\n<li aria-level=\"1\">Replace the existing CORS configuration with the following JSON configuration. This example allows PUT requests from a specific domain (e.g., https:\/\/example.com) and specifies which headers are allowed:<\/li>\r\n<\/ul>\r\n<pre>[<br \/><br \/>\u00a0\u00a0\u00a0\u00a0{<br \/><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"AllowedHeaders\": [<br \/><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"*\"<br \/><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0],<br \/><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"AllowedMethods\": [<br \/><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"PUT\"<br \/><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0],<br \/><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"AllowedOrigins\": [<br \/><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"https:\/\/example.com\"<br \/><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0],<br \/><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"ExposeHeaders\": []<br \/><br \/>\u00a0\u00a0\u00a0\u00a0}<br \/><br \/>]<\/pre>\r\n<h3><b>Attach a Custom Policy to the User:<\/b><b><\/b><\/h3>\r\n<ul>\r\n<li aria-level=\"1\"><b>Create a Custom Policy:<\/b><\/li>\r\n<li aria-level=\"2\">In the IAM Console, go to <b>Policies<\/b>.<\/li>\r\n<li aria-level=\"2\">Click <b>Create policy<\/b>.<\/li>\r\n<li aria-level=\"2\">Select the <b>JSON<\/b> tab and enter a policy that grants permission to upload files to your specific bucket. For example:<\/li>\r\n<\/ul>\r\n<pre>```<br \/><br \/>{<br \/><br \/>\u00a0\u00a0\u00a0\u00a0\"Version\": \"2012-10-17\",<br \/><br \/>\u00a0\u00a0\u00a0\u00a0\"Statement\": [<br \/><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0{<br \/><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"Sid\": \"VisualEditor0\",<br \/><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"Effect\": \"Allow\",<br \/><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"Action\": \"s3:PutObject\",<br \/><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"Resource\": \"arn:aws:s3:::data-from-presign\/*\"<br \/><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}<br \/><br \/>\u00a0\u00a0\u00a0\u00a0]<br \/><br \/>}<\/pre>\r\n<ul>\r\n<li aria-level=\"2\">Click <b>Next: Tags<\/b> (optional) and then <b>Next: Review<\/b>.<\/li>\r\n<li aria-level=\"2\">Provide a name (e.g., S3UploadPolicy) and description.<\/li>\r\n<li aria-level=\"2\">Click <b>Create policy<\/b>.<\/li>\r\n<\/ul>\r\n<h3><b>Attach the Policy to the User<\/b>:<\/h3>\r\n<ul>\r\n<li aria-level=\"1\">Go to <b>Users<\/b> and select the user you created (s3-uploader).<\/li>\r\n<li aria-level=\"1\">Click the <b>Permissions<\/b> tab.<\/li>\r\n<li aria-level=\"1\">Click <b>Add Permissions<\/b>.<\/li>\r\n<li aria-level=\"1\">Select <b>Attach policies directly<\/b>.<\/li>\r\n<li aria-level=\"1\">Search for and select the policy you created (S3UploadPolicy).<\/li>\r\n<li aria-level=\"1\">Click <b>Next: Review<\/b> and then <b>Add permissions<\/b>.<\/li>\r\n<\/ul>\r\n<h3><b>Generate Programmatic Access Credentials:<\/b><b><\/b><\/h3>\r\n<ul>\r\n<li aria-level=\"1\"><b>Get Access Keys<\/b><\/li>\r\n<li aria-level=\"2\">Go to Users and select the user (s3-uploader).<\/li>\r\n<li aria-level=\"2\">Click the Security credentials tab.<\/li>\r\n<li aria-level=\"2\">Under Access keys, click Create access key.<\/li>\r\n<li aria-level=\"2\">Download the CSV file containing the Access key ID and Secret access key or copy them. These are needed for programmatic access.<\/li>\r\n<\/ul>\r\n<h3><b>Generate a Pre-Signed URL:<\/b><\/h3>\r\n<p>Using the AWS SDK (Boto3 for Python), generate a pre-signed URL. Here\u2019s a Python script to do this:<\/p>\r\n<pre class=\"c-mrkdwn__pre\" data-stringify-type=\"pre\">import boto3\r\nimport botocore\r\n\r\n# Assuming your S3 bucket name and image file name\r\nACCESS_KEY = 'access_key'\r\nSECRET_ACCESS_KEY = 'secret_key'\r\nBUCKET_NAME = 'data-from-presign'\r\nOBJECT_KEY = 'image.png'\r\nSTORAGE_CLASS =  'ONEZONE_IA'\r\n\r\n# Initialize a session using the AWS SDK for Python (Boto3)\r\nsession = boto3.Session(\r\n    aws_access_key_id=ACCESS_KEY,\r\n    aws_secret_access_key=SECRET_ACCESS_KEY,\r\n    region_name='ap-south-1'  # Specify the region where your bucket is located\r\n)\r\n\r\n# Generate the presigned URL\r\ns3_client = session.client('s3')\r\ntry:\r\n    response = s3_client.generate_presigned_url(\r\n        'put_object',\r\n        Params={'Bucket': BUCKET_NAME, 'Key': OBJECT_KEY , 'StorageClass': STORAGE_CLASS},\r\n        ExpiresIn=60 \r\n    )\r\nexcept botocore.exceptions.ClientError as e:\r\n    print(e)\r\n    # Handle the error here\r\n\r\nprint(f'Presigned URL to upload file to S3: {response}')<\/pre>\r\n<p><b>Upload a File Using the Pre-Signed URL:<\/b><\/p>\r\n<ul>\r\n<li aria-level=\"1\">Copy the URL generated from the Python script and paste it into Postman. Change the request method to PUT. Additionally, add the following header: {&#8216;x-amz-storage-class&#8217;: &#8216;STANDARD_IA&#8217;}.<\/li>\r\n<li aria-level=\"1\">Navigate to the <b>Body<\/b> tab, select <b>binary<\/b>, and choose the file you want to upload.<\/li>\r\n<\/ul>\r\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-18858 size-full\" src=\"https:\/\/opstree.com\/blog\/wp-content\/uploads\/2024\/08\/unnamed.png\" alt=\"\" width=\"512\" height=\"354\" srcset=\"https:\/\/opstree.com\/blog\/wp-content\/uploads\/2024\/08\/unnamed.png 512w, https:\/\/opstree.com\/blog\/wp-content\/uploads\/2024\/08\/unnamed-300x207.png 300w\" sizes=\"(max-width: 512px) 85vw, 512px\" \/><\/p>\r\n<h2>Wrapping Up<\/h2>\r\n<p>Implementing file uploads using pre-signed URLs is a secure and efficient way to handle file transfers to AWS S3, bypassing the need for direct access credentials. This method not only ensures secure uploads but also streamlines the process for both users and applications. You should consider exploring additional S3 features, such as lifecycle policies for automated data management and versioning for maintaining data integrity.<\/p>\r\n<p><strong>Blog Pundits: <a href=\"https:\/\/www.google.com\/url?sa=t&amp;source=web&amp;rct=j&amp;opi=89978449&amp;url=https:\/\/in.linkedin.com\/in\/deepaksood619&amp;ved=2ahUKEwjxnoep0LyHAxXui68BHQ7LAEwQFnoECBcQAQ&amp;usg=AOvVaw2bCygYqWi3T8TASKoy3-TS\" target=\"_blank\" rel=\"noopener\">Deepak Sood<\/a> and <a href=\"https:\/\/opstree.com\/blog\/\/author\/sandeep7c51ad81ba\/\" target=\"_blank\" rel=\"noreferrer noopener\">Sandeep Rawat<\/a><\/strong><\/p>\r\n<p><!-- \/wp:paragraph -->\r\n\r\n<!-- wp:paragraph --><\/p>\r\n<p><strong>OpsTree is an End-to-End <a href=\"https:\/\/opstree.com\/services\/\" target=\"_blank\" rel=\"noreferrer noopener\">DevOps Solution<\/a> Provider.<\/strong><\/p>\r\n<p><!-- \/wp:paragraph -->\r\n\r\n<!-- wp:buttons --><\/p>\r\n<div class=\"wp-block-buttons\"><!-- wp:button -->\r\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/opstree.com\/contact-us\/?utm_source=WordPress&amp;utm_medium=Blog&amp;utm_campaign=CI%2FCD+with+GitHub+Actions+-+Concepts\" target=\"_blank\" rel=\"noreferrer noopener\">Contact Us<\/a><\/div>\r\n<!-- \/wp:button --><\/div>\r\n<p><!-- \/wp:buttons -->\r\n\r\n<!-- wp:paragraph {\"align\":\"center\"} --><\/p>\r\n<p class=\"has-text-align-center\"><strong>Connect with Us<\/strong><\/p>\r\n<!-- wp:paragraph \/-->","protected":false},"excerpt":{"rendered":"<p>Pre-signed URLs are unique web links designed to provide temporary access to a private resource, such as an Amazon S3 object, without requiring the user to provide their own security credentials. This approach allows you to generate a URL that includes authentication details and permissions, so users or applications can upload files directly to cloud &hellip; <a href=\"https:\/\/opstree.com\/blog\/2024\/08\/06\/uploading-files-using-pre-signed-urls-to-a-specific-storage-class\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Uploading Files Using Pre-Signed URLs to a Specific Storage Class&#8221;<\/span><\/a><\/p>\n","protected":false},"author":244582680,"featured_media":18860,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_coblocks_attr":"","_coblocks_dimensions":"","_coblocks_responsive_height":"","_coblocks_accordion_ie_support":"","jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false},"version":2}},"categories":[36349927],"tags":[768739368],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/opstree.com\/blog\/wp-content\/uploads\/2024\/08\/Uploading-Files-Using-Pre-Signed-URLs-to-a-Specific-Storage-Class-1.png","jetpack_likes_enabled":true,"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/pfDBOm-4TY","jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/opstree.com\/blog\/wp-json\/wp\/v2\/posts\/18846"}],"collection":[{"href":"https:\/\/opstree.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/opstree.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/opstree.com\/blog\/wp-json\/wp\/v2\/users\/244582680"}],"replies":[{"embeddable":true,"href":"https:\/\/opstree.com\/blog\/wp-json\/wp\/v2\/comments?post=18846"}],"version-history":[{"count":7,"href":"https:\/\/opstree.com\/blog\/wp-json\/wp\/v2\/posts\/18846\/revisions"}],"predecessor-version":[{"id":30906,"href":"https:\/\/opstree.com\/blog\/wp-json\/wp\/v2\/posts\/18846\/revisions\/30906"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/opstree.com\/blog\/wp-json\/wp\/v2\/media\/18860"}],"wp:attachment":[{"href":"https:\/\/opstree.com\/blog\/wp-json\/wp\/v2\/media?parent=18846"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/opstree.com\/blog\/wp-json\/wp\/v2\/categories?post=18846"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/opstree.com\/blog\/wp-json\/wp\/v2\/tags?post=18846"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}