Save Excel and PDF to AWS S3.
This commit is contained in:
267
rog/utils.py
267
rog/utils.py
@ -1,10 +1,13 @@
|
||||
import os
|
||||
from aiohttp import ClientError
|
||||
from django.template.loader import render_to_string
|
||||
from django.conf import settings
|
||||
import logging
|
||||
import boto3
|
||||
from django.core.mail import send_mail
|
||||
from django.urls import reverse
|
||||
import uuid
|
||||
import environ
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -111,3 +114,267 @@ def send_invitaion_and_verification_email(user, team, activation_link):
|
||||
subject, body = load_email_template('invitation_and_verification_email.txt', context)
|
||||
share_send_email(subject,body,user.email)
|
||||
|
||||
class S3Bucket:
|
||||
def __init__(self, bucket_name=None, aws_access_key_id=None, aws_secret_access_key=None, region_name=None):
|
||||
self.aws_access_key_id = aws_access_key_id
|
||||
self.aws_secret_access_key = aws_secret_access_key
|
||||
self.region_name = region_name
|
||||
self.bucket_name = bucket_name
|
||||
self.s3_client = self.connect(bucket_name,aws_access_key_id, aws_secret_access_key, region_name)
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return f"s3://{self.bucket_name}"
|
||||
|
||||
def __repr__(self):
|
||||
return f"S3File(bucket_name={self.bucket_name})"
|
||||
|
||||
# AWS S3 への接続
|
||||
def connect(self,bucket_name=None, aws_access_key_id=None, aws_secret_access_key=None, region_name=None):
|
||||
"""
|
||||
S3クライアントの作成
|
||||
|
||||
Args: .env から取得
|
||||
aws_access_key_id (str, optional): AWSアクセスキーID
|
||||
aws_secret_access_key (str, optional): AWSシークレットアクセスキー
|
||||
region_name (str): AWSリージョン名
|
||||
|
||||
Returns:
|
||||
boto3.client: S3クライアント
|
||||
"""
|
||||
try:
|
||||
if aws_access_key_id and aws_secret_access_key:
|
||||
s3_client = boto3.client(
|
||||
's3',
|
||||
aws_access_key_id=aws_access_key_id,
|
||||
aws_secret_access_key=aws_secret_access_key,
|
||||
region_name=region_name
|
||||
)
|
||||
else:
|
||||
env = environ.Env(DEBUG=(bool, False))
|
||||
environ.Env.read_env(env_file=".env")
|
||||
if bucket_name==None:
|
||||
bucket_name = env("S3_BUCKET_NAME")
|
||||
aws_access_key_id = env("AWS_ACCESS_KEY")
|
||||
aws_secret_access_key = env("AWS_SECRET_ACCESS_KEY")
|
||||
region_name = env("S3_REGION")
|
||||
s3_client = boto3.client(
|
||||
's3',
|
||||
aws_access_key_id=aws_access_key_id,
|
||||
aws_secret_access_key=aws_secret_access_key,
|
||||
region_name=region_name
|
||||
)
|
||||
|
||||
return s3_client
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"S3クライアントの作成に失敗しました: {str(e)}")
|
||||
raise
|
||||
|
||||
|
||||
def upload_file(self, file_path, s3_key=None):
|
||||
"""
|
||||
ファイルをS3バケットにアップロード
|
||||
|
||||
Args:
|
||||
file_path (str): アップロードするローカルファイルのパス
|
||||
bucket_name (str): アップロード先のS3バケット名
|
||||
s3_key (str, optional): S3内でのファイルパス(指定がない場合はファイル名を使用)
|
||||
s3_client (boto3.client, optional): S3クライアント
|
||||
|
||||
Returns:
|
||||
bool: アップロードの成功・失敗
|
||||
"""
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
# S3キーが指定されていない場合は、ファイル名を使用
|
||||
if s3_key is None:
|
||||
s3_key = os.path.basename(file_path)
|
||||
|
||||
# S3クライアントが指定されていない場合は新規作成
|
||||
if s3_client is None:
|
||||
s3_client = self.connect()
|
||||
|
||||
# ファイルのアップロード
|
||||
logger.info(f"アップロード開始: {file_path} → s3://{self.bucket_name}/{s3_key}")
|
||||
s3_client.upload_file(file_path, self.bucket_name, s3_key)
|
||||
logger.info("アップロード完了")
|
||||
|
||||
return True
|
||||
|
||||
except FileNotFoundError:
|
||||
logger.error(f"ファイルが見つかりません: {file_path}")
|
||||
return False
|
||||
except ClientError as e:
|
||||
logger.error(f"S3アップロードエラー: {str(e)}")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"予期しないエラーが発生しました: {str(e)}")
|
||||
return False
|
||||
|
||||
def upload_directory(self, directory_path, prefix=''):
|
||||
"""
|
||||
ディレクトリ内のすべてのファイルをS3バケットにアップロード
|
||||
|
||||
Args:
|
||||
directory_path (str): アップロードするローカルディレクトリのパス
|
||||
bucket_name (str): アップロード先のS3バケット名
|
||||
prefix (str, optional): S3内でのプレフィックス(フォルダパス)
|
||||
s3_client (boto3.client, optional): S3クライアント
|
||||
|
||||
Returns:
|
||||
tuple: (成功したファイル数, 失敗したファイル数)
|
||||
"""
|
||||
logger = logging.getLogger(__name__)
|
||||
success_count = 0
|
||||
failure_count = 0
|
||||
|
||||
try:
|
||||
# S3クライアントが指定されていない場合は新規作成
|
||||
if self.s3_client is None:
|
||||
self.s3_client = self.connect()
|
||||
|
||||
# ディレクトリ内のすべてのファイルを処理
|
||||
for root, _, files in os.walk(directory_path):
|
||||
for file in files:
|
||||
local_path = os.path.join(root, file)
|
||||
|
||||
# S3キーの作成(相対パスを維持)
|
||||
relative_path = os.path.relpath(local_path, directory_path)
|
||||
s3_key = os.path.join(prefix, relative_path).replace('\\', '/')
|
||||
|
||||
# ファイルのアップロード
|
||||
if self.upload_file(local_path, s3_key):
|
||||
success_count += 1
|
||||
else:
|
||||
failure_count += 1
|
||||
|
||||
logger.info(f"アップロード完了: 成功 {success_count} 件, 失敗 {failure_count} 件")
|
||||
return success_count, failure_count
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"ディレクトリのアップロードに失敗しました: {str(e)}")
|
||||
return success_count, failure_count
|
||||
|
||||
def download_file(self, s3_key, file_path):
|
||||
"""
|
||||
S3バケットからファイルをダウンロード
|
||||
|
||||
Args:
|
||||
bucket_name (str): ダウンロード元のS3バケット名
|
||||
s3_key (str): ダウンロードするファイルのS3キー
|
||||
file_path (str): ダウンロード先のローカルファイルパス
|
||||
s3_client (boto3.client, optional): S3クライアント
|
||||
|
||||
Returns:
|
||||
bool: ダウンロードの成功・失敗
|
||||
"""
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
# S3クライアントが指定されていない場合は新規作成
|
||||
if self.s3_client is None:
|
||||
self.s3_client = self.connect_to_s3()
|
||||
|
||||
# ファイルのダウンロード
|
||||
logger.info(f"ダウンロード開始: s3://{self.bucket_name}/{s3_key} → {file_path}")
|
||||
self.s3_client.download_file(self.bucket_name, s3_key, file_path)
|
||||
logger.info("ダウンロード完了")
|
||||
|
||||
return True
|
||||
|
||||
except FileNotFoundError:
|
||||
logger.error(f"ファイルが見つかりません: s3://{self.bucket_name}/{s3_key}")
|
||||
return False
|
||||
except ClientError as e:
|
||||
logger.error(f"S3ダウンロードエラー: {str(e)}")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"予期しないエラーが発生しました: {str(e)}")
|
||||
return False
|
||||
|
||||
def download_directory(self, prefix, directory_path):
|
||||
"""
|
||||
S3バケットからディレクトリをダウンロード
|
||||
|
||||
Args:
|
||||
bucket_name (str): ダウンロード元のS3バケット名
|
||||
prefix (str): ダウンロードするディレクトリのプレフィックス(フォルダパス)
|
||||
directory_path (str): ダウンロード先のローカルディレクトリパス
|
||||
s3_client (boto3.client, optional): S3クライアント
|
||||
|
||||
Returns:
|
||||
tuple: (成功したファイル数, 失敗したファイル数)
|
||||
"""
|
||||
logger = logging.getLogger(__name__)
|
||||
success_count = 0
|
||||
failure_count = 0
|
||||
|
||||
try:
|
||||
# S3クライアントが指定されていない場合は新規作成
|
||||
if s3_client is None:
|
||||
s3_client = self.connect()
|
||||
|
||||
# プレフィックスに一致するオブジェクトをリスト
|
||||
paginator = s3_client.get_paginator('list_objects_v2')
|
||||
pages = paginator.paginate(Bucket=self.bucket_name, Prefix=prefix)
|
||||
|
||||
for page in pages:
|
||||
if 'Contents' in page:
|
||||
for obj in page['Contents']:
|
||||
s3_key = obj['Key']
|
||||
relative_path = os.path.relpath(s3_key, prefix)
|
||||
local_path = os.path.join(directory_path, relative_path)
|
||||
|
||||
# ローカルディレクトリが存在しない場合は作成
|
||||
local_dir = os.path.dirname(local_path)
|
||||
if not os.path.exists(local_dir):
|
||||
os.makedirs(local_dir)
|
||||
|
||||
# ファイルのダウンロード
|
||||
if self.download_file(self.bucket_name, s3_key, local_path):
|
||||
success_count += 1
|
||||
else:
|
||||
failure_count += 1
|
||||
|
||||
logger.info(f"ダウンロード完了: 成功 {success_count} 件, 失敗 {failure_count} 件")
|
||||
return success_count, failure_count
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"ディレクトリのダウンロードに失敗しました: {str(e)}")
|
||||
return success_count, failure_count
|
||||
|
||||
def delete_object(self, s3_key):
|
||||
"""
|
||||
S3バケットからオブジェクトを削除
|
||||
|
||||
Args:
|
||||
bucket_name (str): 削除するオブジェクトが存在するS3バケット名
|
||||
s3_key (str): 削除するオブジェクトのS3キー
|
||||
s3_client (boto3.client, optional): S3クライアント
|
||||
|
||||
Returns:
|
||||
bool: 削除の成功・失敗
|
||||
"""
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
# S3クライアントが指定されていない場合は新規作成
|
||||
if s3_client is None:
|
||||
s3_client = self.connect()
|
||||
|
||||
# オブジェクトの削除
|
||||
logger.info(f"削除開始: s3://{self.bucket_name}/{s3_key}")
|
||||
s3_client.delete_object(Bucket=self.bucket_name, Key=s3_key)
|
||||
logger.info("削除完了")
|
||||
|
||||
return True
|
||||
|
||||
except ClientError as e:
|
||||
logger.error(f"S3削除エラー: {str(e)}")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"予期しないエラーが発生しました: {str(e)}")
|
||||
return False
|
||||
|
||||
|
||||
16
rog/views.py
16
rog/views.py
@ -17,7 +17,7 @@ import requests
|
||||
from rest_framework import serializers
|
||||
from django.db import IntegrityError
|
||||
from django.urls import reverse
|
||||
from .utils import send_verification_email,send_invitation_email,send_team_join_email,send_reset_password_email
|
||||
from .utils import S3Bucket, send_verification_email,send_invitation_email,send_team_join_email,send_reset_password_email
|
||||
from django.conf import settings
|
||||
import uuid
|
||||
from rest_framework.exceptions import ValidationError as DRFValidationError
|
||||
@ -2817,13 +2817,15 @@ def export_excel(request, zekken_number, event_code):
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||
)
|
||||
|
||||
# PDFファイルを読み込んでレスポンスを返す
|
||||
with open(pdf_path, 'rb') as pdf_file:
|
||||
pdf_content = pdf_file.read()
|
||||
s3 = S3Bucket('sumasenrogaining')
|
||||
s3.upload_file(pdf_path, f'{event_code}/scoreboard', f'certificate_{zekken_number}.pdf')
|
||||
s3.upload_file(excel_path, f'{event_code}/scoreboard_excel', f'certificate_{zekken_number}.xlsx')
|
||||
|
||||
os.remove(excel_path)
|
||||
os.remove(pdf_path)
|
||||
|
||||
return Response( status=status.HTTP_200_OK )
|
||||
|
||||
response = HttpResponse(pdf_content, content_type='application/pdf')
|
||||
response['Content-Disposition'] = f'attachment; filename="certificate_{zekken_number}_{event_code}.pdf"'
|
||||
return response
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
logger.error(f"Error converting to PDF: {str(e)}\nSTDOUT: {e.stdout}\nSTDERR: {e.stderr}")
|
||||
|
||||
Reference in New Issue
Block a user