-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Yujeong/materials 기본 틀 구현 #140
Conversation
파일 업로드 및 스트리밍 - boto3, botocore 사용 - AWS S3 에 저장 및 참조
- AWS S3 스토리지 설정 추가
# GET 요청: 특정 이미지 파일을 조회합니다. | ||
# PUT 요청: 특정 이미지 파일을 변경합니다. | ||
# DELETE 요청: 특정 이미지 파일을 삭제합니다. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이런 형태의 주석이 표시가 안될 것 같아요. 아래 docstring 주석으로 변경하면 어떨까요?
"""
GET 요청: 특정 이미지 파일을 조회합니다.
PUT 요청: 특정 이미지 파일을 변경합니다.
DELETE 요청: 특정 이미지 파일을 삭제합니다.
"""
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
네. 아니면 extend_schema_view 데코레이션을 추가하시는 것도 하나의 고려사항이 될수도 있을 것 같습니다
materials/views.py
Outdated
class VideoCreateView(generics.CreateAPIView): | ||
# POST 요청: 영상 파일을 업로드합니다. | ||
|
||
queryset = Video.objects.all() | ||
serializer_class = VideoSerializer | ||
permission_classes = [] | ||
parser_classes = (MultiPartParser, FormParser) | ||
|
||
def create(self, request, *args, **kwargs): | ||
serializer = self.get_serializer(data=request.data) | ||
if serializer.is_valid(): | ||
file = request.FILES.get("file") | ||
if not file: | ||
return Response( | ||
{"error": "No file provided"}, status=status.HTTP_400_BAD_REQUEST | ||
) | ||
|
||
# S3 클라이언트 설정 | ||
s3_client = boto3.client( | ||
"s3", | ||
aws_access_key_id=settings.AWS_ACCESS_KEY_ID, | ||
aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY, | ||
region_name=settings.AWS_S3_REGION_NAME, | ||
) | ||
|
||
try: | ||
# S3에 파일 업로드 | ||
file_name = f"videos/{file.name}" | ||
s3_client.upload_fileobj( | ||
file, | ||
settings.AWS_STORAGE_BUCKET_NAME, | ||
file_name, | ||
ExtraArgs={"ContentType": file.content_type}, | ||
) | ||
|
||
# 업로드된 파일의 URL 생성 | ||
file_url = f"https://{settings.AWS_S3_CUSTOM_DOMAIN}/{file_name}" | ||
|
||
# 비디오 객체 생성 및 저장 | ||
video = serializer.save(file=file_url) | ||
|
||
return Response( | ||
self.get_serializer(video).data, status=status.HTTP_201_CREATED | ||
) | ||
except ClientError as e: | ||
return Response( | ||
{"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR | ||
) | ||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
중복 코드입니다.
materials/views.py
Outdated
class VideoListCreateView(generics.ListCreateAPIView): | ||
# GET 요청: 영상 파일 목록을 조회합니다. | ||
|
||
queryset = Video.objects.all() | ||
serializer_class = VideoSerializer | ||
permission_classes = [] | ||
permission_classes = [] | ||
|
||
def perform_create(self, serializer): | ||
serializer.save(topic_id=self.request.data.get("topic_id")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
createView가 있기 때문에 ListAPIView만 사용하면 어떨까요 ? 아니면 위 createView를 이 View와 합쳐야 할 것 같아요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
네 두 view를 합치는 쪽이 괜찮아 보일 것 같습니다.
그런데 course를 조회하는 view가 있는데, 굳이 video들을 따로 조회하는 view가 필요할까요? 어차피 사용자들은 curriculum이나 course 단위로 상품을 보는게 아닌가 싶어서요. 관리자를 위한 view인걸까요?
|
||
def retrieve(self, request, *args, **kwargs): | ||
instance = self.get_object() | ||
if request.query_params.get("stream", "").lower() == "true": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s3도 stream 허용한가요? 신기하네요!!
def retrieve(self, request, *args, **kwargs): | ||
instance = self.get_object() | ||
if request.query_params.get("stream", "").lower() == "true": | ||
return self.stream_video(instance) | ||
serializer = self.get_serializer(instance) | ||
return Response(serializer.data) | ||
|
||
def stream_video(self, video): | ||
s3 = boto3.client( | ||
"s3", | ||
aws_access_key_id=settings.AWS_ACCESS_KEY_ID, | ||
aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY, | ||
region_name=settings.AWS_S3_REGION_NAME, | ||
) | ||
|
||
file_key = str(video.file) # S3에 저장된 파일의 키 | ||
|
||
def generate(): | ||
offset = 0 | ||
chunk_size = 8388608 # 8MB 청크 크기 | ||
while True: | ||
try: | ||
file_object = s3.get_object( | ||
Bucket=settings.AWS_STORAGE_BUCKET_NAME, | ||
Key=file_key, | ||
Range=f"bytes={offset}-{offset+chunk_size-1}", | ||
) | ||
data = file_object["Body"].read() | ||
if not data: | ||
break | ||
yield data | ||
offset += len(data) | ||
except Exception as e: | ||
print(f"영상 스트림 실패: {str(e)}") | ||
break | ||
|
||
response = StreamingHttpResponse( | ||
streaming_content=generate(), content_type="video/mp4" | ||
) | ||
response["Content-Disposition"] = f'inline; filename="{video.title}.mp4"' | ||
return response |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
간단히 설명해주세요.
img.verify() | ||
except: | ||
return Response( | ||
{"error": "Invalid image file"}, status=status.HTTP_400_BAD_REQUEST |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
나중에 에러 메세지를 한국어로 수정해주시면 좋을 것 같습니다!
# GET 요청: 특정 이미지 파일을 조회합니다. | ||
# PUT 요청: 특정 이미지 파일을 변경합니다. | ||
# DELETE 요청: 특정 이미지 파일을 삭제합니다. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
네. 아니면 extend_schema_view 데코레이션을 추가하시는 것도 하나의 고려사항이 될수도 있을 것 같습니다
materials/views.py
Outdated
class VideoListCreateView(generics.ListCreateAPIView): | ||
# GET 요청: 영상 파일 목록을 조회합니다. | ||
|
||
queryset = Video.objects.all() | ||
serializer_class = VideoSerializer | ||
permission_classes = [] | ||
permission_classes = [] | ||
|
||
def perform_create(self, serializer): | ||
serializer.save(topic_id=self.request.data.get("topic_id")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
네 두 view를 합치는 쪽이 괜찮아 보일 것 같습니다.
그런데 course를 조회하는 view가 있는데, 굳이 video들을 따로 조회하는 view가 필요할까요? 어차피 사용자들은 curriculum이나 course 단위로 상품을 보는게 아닌가 싶어서요. 관리자를 위한 view인걸까요?
weaverse/settings.py
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
나중에 .env에 추가할 내용을 README나 Wiki에 추가해주시면 감사하겠습니다. 사실 근데 이부분은 Wiki에만 넣는게 좋지 않을가 싶은데, 다른 분들 생각이 궁금하네요.
settings.py
urls.py
models.py
serializers.py
views.py