안녕하세요. 이번 시간에는 AWS S3의 Presigned URL에 대해 자세히 알아보는 시간을 가지겠습니다.
Presigned URL은 AWS S3와 같은 클라우드 스토리지 혹은 MINIO 오브젝트 스토리지에서 제공하는 기능으로, 다른 사람이 해당 스토리지에 대한 보안 자격 증명 혹은 권한 없이 객체를 업로드하거나 다운로드할 수 있도록 합니다. 이는 API key를 직접적으로 공유하지 않고도 다른 유저에게 객체에 접근 권한을 임시적으로 부여할 수 있다는 장점이 있습니다.
보안성
효율성
유연성
파일 공유 시스템
업로드 시스템
미디어 스트리밍
import logging
import boto3
from botocore.exceptions import ClientError
import os
import requests
from urllib.parse import urlparse
# 로깅 설정
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# S3 클라이언트 초기화
def initialize_s3_client():
"""Initialize S3 client with environment variables
Required environment variables:
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
- AWS_DEFAULT_REGION
"""
try:
s3_client = boto3.client(
's3',
aws_access_key_id=os.getenv('AWS_ACCESS_KEY_ID'),
aws_secret_access_key=os.getenv('AWS_SECRET_ACCESS_KEY'),
region_name=os.getenv('AWS_DEFAULT_REGION', 'ap-northeast-2')
)
return s3_client
except Exception as e:
logger.error(f"Failed to initialize S3 client: {e}")
return None
def create_presigned_url(bucket_name, object_name, s3_client, expiration=3600, method='get_object', content_type=None):
"""Generate a presigned URL for S3 object upload or download
:param bucket_name: string
:param object_name: string
:param s3_client: boto3.client
:param expiration: Time in seconds for the presigned URL to remain valid
:param method: S3 method ('get_object' for download, 'put_object' for upload)
:param content_type: string, optional. e.g., 'image/jpeg', 'application/pdf'
:return: Presigned URL as string. If error, returns None.
"""
try:
params = {
'Bucket': bucket_name,
'Key': object_name
}
# Content-Type 설정 (업로드의 경우)
if method == 'put_object' and content_type:
params['ContentType'] = content_type
response = s3_client.generate_presigned_url(
method,
Params=params,
ExpiresIn=expiration,
HttpMethod='https' # HTTPS 강제
)
logger.info(f"Presigned URL generated for {method}")
return response
except ClientError as e:
logger.error(f"Error generating presigned URL: {e}")
return None
def upload_file_with_presigned_url(presigned_url, file_path, content_type=None):
"""Upload a file using presigned URL
:param presigned_url: Presigned URL for upload
:param file_path: Local path of file to upload
:param content_type: string, optional. e.g., 'image/jpeg'
:return: Boolean indicating success/failure
"""
try:
with open(file_path, 'rb') as f:
headers = {}
if content_type:
headers['Content-Type'] = content_type
response = requests.put(presigned_url,
data=f.read(),
headers=headers)
if response.status_code == 200:
logger.info(f"Successfully uploaded file: {file_path}")
return True
else:
logger.error(f"Failed to upload file. Status code: {response.status_code}")
return False
except Exception as e:
logger.error(f"Error uploading file: {e}")
return False
def download_file_with_presigned_url(presigned_url, local_path):
"""Download a file using presigned URL with proper error handling
:param presigned_url: Presigned URL for download
:param local_path: Local path to save the downloaded file
:return: Boolean indicating success/failure
"""
try:
response = requests.get(presigned_url)
if response.status_code == 200:
with open(local_path, 'wb') as f:
f.write(response.content)
logger.info(f"Successfully downloaded file to: {local_path}")
return True
else:
logger.error(f"Failed to download file. Status code: {response.status_code}")
return False
except Exception as e:
logger.error(f"Error downloading file: {e}")
return False
def main():
# 환경 변수 설정 확인
required_env_vars = ['AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY']
for var in required_env_vars:
if not os.getenv(var):
logger.error(f"Missing required environment variable: {var}")
return
# S3 클라이언트 초기화
s3_client = initialize_s3_client()
if not s3_client:
return
# 설정
bucket_name = "your-bucket-name"
object_name = "path/to/your/file.jpg"
local_file = "local_file.jpg"
# 이미지 업로드를 위한 Presigned URL 생성
upload_url = create_presigned_url(
bucket_name,
object_name,
s3_client,
method='put_object',
expiration=3600, # 1시간
content_type='image/jpeg'
)
# 파일 업로드
if upload_url:
success = upload_file_with_presigned_url(
upload_url,
local_file,
content_type='image/jpeg'
)
print(f"Upload success: {success}")
if __name__ == "__main__":
main()
URL 만료 시간 설정
HTTPS 사용
Content-Type 제한
URL 공유 주의사항
오늘은 Presigned-URL 에 대해서 알아봤습니다. 감사합니다.