Skip to content

Commit

Permalink
Feat/23/service photo (#54)
Browse files Browse the repository at this point in the history
* #31 fix: modify migrations

* #9 fix : modify selector

* Merge branch '31/fix/server_api' of https://github.com/chaechaeis/UPCY_BE into 31/fix/server_api

* #23 feat : upload photo in s3 socket

---------

Co-authored-by: 밍 <[email protected]>
  • Loading branch information
chaechaeis and 0321minji authored Jun 5, 2024
1 parent d975a66 commit 87ad1b2
Show file tree
Hide file tree
Showing 9 changed files with 160 additions and 23 deletions.
9 changes: 9 additions & 0 deletions UpcyProject/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from pathlib import Path
import environ
import os
from dotenv import load_dotenv

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
Expand Down Expand Up @@ -169,3 +170,11 @@
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

#s3 setting
load_dotenv()

AWS_ACCESS_KEY_ID = os.getenv('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = os.getenv('AWS_SECRET_ACCESS_KEY')
AWS_STORAGE_BUCKET_NAME = os.environ.get('AWS_STORAGE_BUCKET_NAME', 'cognisle-bucket')
AWS_S3_REGION_NAME = os.environ.get('AWS_S3_REGION_NAME', 'ap-northeast-2')
51 changes: 51 additions & 0 deletions core/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""
포트폴리오, 룩북, 서비스 모두 사용할 수 있도록 공통 함수로 따로 제작.
services>services.py>ServicePhotoApi 참고해서 작성하면 됨
"""

import io
import boto3
import uuid
from django.conf import settings
from UpcyProject import settings
from datetime import datetime


def get_random_text(length):
return str(uuid.uuid4()).replace('-', '')[:length]

def s3_file_upload_by_file_data(upload_file, region_name, bucket_name, bucket_path, content_type=None):
bucket_name = bucket_name.replace('/', '')
# 파일 확장자 추출
if content_type:
content_type = content_type
else:
content_type = upload_file.content_type

now = datetime.now()
random_str = get_random_text(20)
extension = upload_file.name.split('.')[-1]

random_file_name = '{}.{}'.format(str(now.strftime('%Y%m%d%H%M%S'))+random_str,extension)
# 파일의 ContentType을 설정
random_file_name = f"{now.strftime('%Y%m%d%H%M%S')}_{random_str}.{extension}"
upload_file_path_name = f"{bucket_path}/{random_file_name}"

s3 = boto3.resource('s3', region_name=region_name, aws_access_key_id=settings.AWS_ACCESS_KEY_ID, aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY)

try:
# 버킷이 존재하는지 확인
s3.meta.client.head_bucket(Bucket=bucket_name)
except s3.meta.client.exceptions.NoSuchBucket:
raise Exception(f"The specified bucket does not exist: {bucket_name}")

file_data = io.BytesIO(upload_file.read())

try:
# 파일을 S3에 업로드
s3.Bucket(bucket_name).put_object(Key=upload_file_path_name, Body=file_data, ContentType=content_type, ACL='public-read')
print(extension)
return f"https://{bucket_name}.s3.{region_name}.amazonaws.com/{upload_file_path_name}"
except Exception as e:
print(f"Failed to upload file to S3: {e}")
return False
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='product',
name='fit',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='products', to='products.fit'),
field=models.ForeignKey(blank=False, null=False, on_delete=django.db.models.deletion.CASCADE, related_name='products', to='products.fit'),
),
migrations.AddField(
model_name='product',
Expand Down
3 changes: 1 addition & 2 deletions products/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ def create(self, name : str,keywords : list[str],basic_price : str,option : str,
category : str, style : list[str], texture : list[str], fit : list[str], detail:list[str],
) -> Product:
product_service=ProductService()

product= product_service.create(
reformer=self.user,

Expand All @@ -46,8 +45,8 @@ def create(self, name : str,keywords : list[str],basic_price : str,option : str,
transaction_package=transaction_package,
refund=refund,
)

if product is not None:
print('here')
ProductPhotoService.process_photos(product=product, product_photos=product_photos )
ProductKeywordService.process_keywords(product=product, keywords=keywords)
return product
Expand Down
1 change: 0 additions & 1 deletion products/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ def post(self,request):
transaction_package=data.get('transaction_package'),
refund=data.get('refund'),
)

if product is not None:
return Response({
'status' : 'success',
Expand Down
15 changes: 12 additions & 3 deletions services/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from django.db import models
from core.models import TimeStampedModel
from django.conf import settings # s3를 이용한 이미지 업로드용
from datetime import datetime # s3를 이용한 이미지 업로드용

# Create your models here.
class Category(models.Model):
Expand Down Expand Up @@ -54,11 +56,18 @@ class ServiceKeyword(models.Model):


#Service_Photo 모델 만들기
# get_service_photo_upload_path 함수는 수정 가능성 있음
def get_service_photo_upload_path(instance, filename):
return 'services/photo/{}'.format(filename)
# get_service_photo_upload_path 함수가 꼭 필요할까?

#class ServicePhoto(models.Model):
# image = models.ImageField(
# upload_to=get_service_photo_upload_path, default='service_photo.png')
# service = models.ForeignKey(
# 'Service', related_name='service_photos', on_delete=models.CASCADE, null=True, blank=True)

class ServicePhoto(models.Model):
image = models.ImageField(
upload_to=get_service_photo_upload_path, default='service_photo.png')
image = models.URLField(default='service_photo.png')
service = models.ForeignKey(
'Service', related_name='service_photos', on_delete=models.CASCADE, null=True, blank=True)
'Service', related_name='service_photos', on_delete=models.CASCADE, null=True, blank=True)
6 changes: 4 additions & 2 deletions services/selectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class ServiceDto:
notice : str = None

keywords: list[str] = None
photos: list[str] = None
photos: str = None #원래는 list[str]인데, 일단 service 하나당 photo 하나만 추가할 수 있는 것으로
nickname : str = None
market:str = None

Expand Down Expand Up @@ -192,5 +192,7 @@ def list(search:str, order:str, user:User,
) for service in services]

return services_dtos
def likes(self, service:Service, user:User):
def likes(self, service, user:User):
from services.models import Service
from users.models import User
return service.likeuser_set.filter(pk=user.pk).exists()
81 changes: 71 additions & 10 deletions services/services.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import io
import time
import uuid

import boto3
import io
from django.shortcuts import get_list_or_404, get_object_or_404
from django.db import transaction
from django.core.files.images import ImageFile
Expand All @@ -11,6 +10,8 @@
from services.models import Service, ServiceKeyword, ServicePhoto, Category, Style, Fit, Texture, Detail
from .selectors import ServiceSelector
from users.models import User
from core.utils import s3_file_upload_by_file_data
from UpcyProject import settings


class ServiceCoordinatorService:
Expand Down Expand Up @@ -110,22 +111,81 @@ def create(name : str,basic_price : str, max_price: str, option : str,info : str
service.detail.set(detail)
return service

class ServicePhotoService:
def __init__(self, file):
self.file = file

def upload(self):
s3_client = boto3.client(
's3',
aws_access_key_id = settings.AWS_ACCESS_KEY_ID,
aws_secret_access_key = settings.AWS_SECRET_ACCESS_KEY
)
extension = self.file.name.split('.')[-1]
url = f'services/photo/{uuid.uuid1().hex}.{extension}'

file_data = io.BytesIO(self.file.read())
file_data.seek(0)

try:
s3_client.upload_fileobj(
file_data, # 파일 데이터 객체
"cognisle-bucket",
url,
ExtraArgs={
"ContentType": self.file.content_type
}
)
return f"https://cognisle-bucket.s3.{settings.AWS_S3_REGION_NAME}.amazonaws.com/{url}"

except Exception as e:
print(f"Failed to upload file to S3: {e}")
return None

@staticmethod
def process_photos(service:Service, service_photos: list[str]):
for service_photo in service_photos:
op, photo_url = service_photo.split(',')

photo_path = photo_url.replace(settings.MEDIA_ROOT,'').lstrip('/')

try:
service_photo, created = ServicePhoto.objects.get_or_create(image=photo_path)
except Exception as e:
print(f"Error in process_photos: {e}")
service_photo = None

if op == 'add' and service_photo :
service_photo.service = service
service_photo.full_clean()
service_photo.save()
elif op == 'remove' and service_photo :
service_photo.delete()

"""
class ServicePhotoService:
def __init__(self):
pass
@staticmethod
def create(image:InMemoryUploadedFile):
ext = image.name.split(".")[-1]
file_path = '{}.{}'.format(str(time.time())+str(uuid.uuid4().hex),ext)
image = ImageFile(io.BytesIO(image.read()),name=file_path)
service_photo = ServicePhoto(image=image, service=None)
s3_url = s3_file_upload_by_file_data(
upload_file=image,
region_name=settings.AWS_S3_REGION_NAME,
bucket_name=settings.AWS_STORAGE_BUCKET_NAME,
bucket_path='services/photo' # 고유한 경로 지정
)
if not s3_url:
raise Exception("Failed to upload image to S3")
service_photo = ServicePhoto(image=s3_url, service=None)
service_photo.full_clean()
service_photo.save()
return settings.MEDIA_URL + service_photo.image.name
return service_photo.image #위아래 return 중 뭐가 맞는지 모르겠음
return settings.MEDIA_URL + service_photo.image.name #위아래 return 중 뭐가 맞는지 모르겠음
@staticmethod
def process_photos(service:Service, service_photos: list[str]):
for service_photo in service_photos:
Expand All @@ -139,12 +199,13 @@ def process_photos(service:Service, service_photos: list[str]):
print(f"Error in process_photos: {e}")
service_photo = None
if op == 'add':
if op == 'add' and service_photo :
service_photo.service = service
service_photo.full_clean()
service_photo.save()
elif op == 'remove':
elif op == 'remove' and service_photo :
service_photo.delete()
"""

class ServiceKeywordService:
def __init__(self):
Expand Down
15 changes: 11 additions & 4 deletions services/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,14 +146,21 @@ def post(self, request):
serializers = self.ServicePhotoCreateInputSerializer(data=request.data)
serializers.is_valid(raise_exception=True)
data = serializers.validated_data

image_file = data.get('image')


service_photo_service = ServicePhotoService(file=image_file)

service_photo_url = service_photo_service.upload()

service_photo_url = ServicePhotoService.create(
image=data.get('image')
)
#service_photo_url = ServicePhotoService.upload(
# image=data.get('image')
#)

return Response({
'status':'success',
'data':{'location': service_photo_url},
'data':{'location': service_photo_url},
}, status = status.HTTP_201_CREATED)

class ServiceDetailApi(APIView):
Expand Down

0 comments on commit 87ad1b2

Please sign in to comment.