Refactor: file api in to seperate api app & use drf api views
This commit is contained in:
parent
1f75b1bf17
commit
bd2791c155
6 changed files with 150 additions and 71 deletions
0
backend/api/files/__init__.py
Normal file
0
backend/api/files/__init__.py
Normal file
32
backend/api/files/serializers.py
Normal file
32
backend/api/files/serializers.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
from rest_framework import serializers
|
||||||
|
from apps.files.models import PostFileModel
|
||||||
|
|
||||||
|
|
||||||
|
class PostFileSerializer(serializers.ModelSerializer):
|
||||||
|
"""Serializer for PostFileModel."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = PostFileModel
|
||||||
|
fields = ["hash_blake3", "file_type", "file", "thumbnail"]
|
||||||
|
# Add any other fields you need
|
||||||
|
|
||||||
|
def to_representation(self, instance):
|
||||||
|
"""Customize the representation of the model."""
|
||||||
|
representation = super().to_representation(instance)
|
||||||
|
|
||||||
|
# Add file name from related model
|
||||||
|
try:
|
||||||
|
representation["filename"] = instance.name.first().filename
|
||||||
|
except (AttributeError, IndexError):
|
||||||
|
representation["filename"] = "Unknown"
|
||||||
|
|
||||||
|
# Add URLs for different thumbnail sizes
|
||||||
|
base_url = f"/api/files/{instance.hash_blake3}/"
|
||||||
|
thumbnails = {}
|
||||||
|
for size_key in THUMBNAIL_SIZES:
|
||||||
|
thumbnails[size_key] = f"{base_url}?t={size_key}"
|
||||||
|
|
||||||
|
representation["thumbnails"] = thumbnails
|
||||||
|
representation["download_url"] = f"{base_url}?d=0"
|
||||||
|
|
||||||
|
return representation
|
9
backend/api/files/urls.py
Normal file
9
backend/api/files/urls.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
from django.urls import path
|
||||||
|
from .views import FileServeView, FileDetailView
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
# Serve the actual file
|
||||||
|
path("files/<str:file_hash>/", FileServeView.as_view(), name="serve_file"),
|
||||||
|
# Get file metadata
|
||||||
|
path("files/<str:file_hash>/info/", FileDetailView.as_view(), name="file_info"),
|
||||||
|
]
|
108
backend/api/files/views.py
Normal file
108
backend/api/files/views.py
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
import os
|
||||||
|
from django.conf import settings
|
||||||
|
from django.http import FileResponse
|
||||||
|
from rest_framework import status
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.permissions import IsAuthenticated
|
||||||
|
from sorl.thumbnail import get_thumbnail
|
||||||
|
from apps.files.models import PostFileModel
|
||||||
|
from .serializers import PostFileSerializer # You'll need to create this
|
||||||
|
|
||||||
|
THUMBNAIL_SIZES = {
|
||||||
|
"sx": (64, ".thumb_64.jpg"),
|
||||||
|
"sm": (256, ".thumb_256.jpg"),
|
||||||
|
"md": (748, ".thumb_748.jpg"),
|
||||||
|
"lg": (1024, ".thumb_1024.jpg"),
|
||||||
|
"xl": (2048, ".thumb_2048.jpg"),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class FileServeView(APIView):
|
||||||
|
"""
|
||||||
|
API view to serve content files for download or inline viewing.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Uncomment the following line if authentication is required
|
||||||
|
# permission_classes = [IsAuthenticated]
|
||||||
|
|
||||||
|
def get_thumbnail_file(self, source_path, size_key):
|
||||||
|
"""Generates and retrieves the thumbnail file."""
|
||||||
|
size, suffix = THUMBNAIL_SIZES.get(size_key, (None, ""))
|
||||||
|
if size:
|
||||||
|
thumbnail_file = get_thumbnail(source_path, str(size), upscale=False)
|
||||||
|
return os.path.abspath(
|
||||||
|
os.path.join(settings.MEDIA_ROOT, thumbnail_file.name)
|
||||||
|
), suffix
|
||||||
|
return None, ""
|
||||||
|
|
||||||
|
def get(self, request, file_hash):
|
||||||
|
"""Handle GET requests for file serving."""
|
||||||
|
download = request.query_params.get("d") == "0"
|
||||||
|
thumbnail_key = request.query_params.get("t")
|
||||||
|
|
||||||
|
try:
|
||||||
|
obj_file = PostFileModel.objects.filter(hash_blake3=file_hash).first()
|
||||||
|
if not obj_file:
|
||||||
|
return Response(
|
||||||
|
{"error": "File not found"}, status=status.HTTP_404_NOT_FOUND
|
||||||
|
)
|
||||||
|
|
||||||
|
file_name = obj_file.name.first().filename
|
||||||
|
file_type = obj_file.file_type
|
||||||
|
source_file = obj_file.file
|
||||||
|
|
||||||
|
# Use thumbnail if requested and file type is image
|
||||||
|
if thumbnail_key and file_type != "image":
|
||||||
|
source_file = obj_file.thumbnail
|
||||||
|
|
||||||
|
# Retrieve the requested thumbnail file if applicable
|
||||||
|
if thumbnail_key in THUMBNAIL_SIZES:
|
||||||
|
thumbnail_path, suffix = self.get_thumbnail_file(
|
||||||
|
source_file.path, thumbnail_key
|
||||||
|
)
|
||||||
|
if thumbnail_path:
|
||||||
|
file_name += suffix
|
||||||
|
file = open(thumbnail_path, "rb")
|
||||||
|
else:
|
||||||
|
file = source_file.file
|
||||||
|
else:
|
||||||
|
file = source_file.file
|
||||||
|
|
||||||
|
response = FileResponse(file)
|
||||||
|
disposition_type = "attachment" if download else "inline"
|
||||||
|
response["Content-Disposition"] = (
|
||||||
|
f'{disposition_type}; filename="{file_name}"'
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return Response(
|
||||||
|
{"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class FileDetailView(APIView):
|
||||||
|
"""
|
||||||
|
API view to get file metadata without serving the actual file.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Uncomment the following line if authentication is required
|
||||||
|
# permission_classes = [IsAuthenticated]
|
||||||
|
|
||||||
|
def get(self, request, file_hash):
|
||||||
|
"""Return file metadata."""
|
||||||
|
try:
|
||||||
|
obj_file = PostFileModel.objects.filter(hash_blake3=file_hash).first()
|
||||||
|
if not obj_file:
|
||||||
|
return Response(
|
||||||
|
{"error": "File not found"}, status=status.HTTP_404_NOT_FOUND
|
||||||
|
)
|
||||||
|
|
||||||
|
serializer = PostFileSerializer(obj_file)
|
||||||
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return Response(
|
||||||
|
{"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||||
|
)
|
|
@ -1,6 +1,5 @@
|
||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
|
|
||||||
from apps.files.views import serve_content_file
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("schema/", include("api.schema.urls")),
|
path("schema/", include("api.schema.urls")),
|
||||||
|
@ -8,5 +7,5 @@ urlpatterns = [
|
||||||
path("user/", include("api.user.urls")),
|
path("user/", include("api.user.urls")),
|
||||||
path("posts/", include("api.posts.urls")),
|
path("posts/", include("api.posts.urls")),
|
||||||
path("creators/", include("api.creators.urls")),
|
path("creators/", include("api.creators.urls")),
|
||||||
path("files/<str:file_hash>/", serve_content_file, name="serve_file"),
|
path("files/", include("api.files.urls")),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,69 +0,0 @@
|
||||||
import os
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.shortcuts import get_object_or_404
|
|
||||||
from django.http import HttpResponse, FileResponse
|
|
||||||
|
|
||||||
from sorl.thumbnail import get_thumbnail
|
|
||||||
|
|
||||||
from .models import PostFileModel
|
|
||||||
|
|
||||||
|
|
||||||
THUMBNAIL_SIZES = {
|
|
||||||
"sx": (64, ".thumb_64.jpg"),
|
|
||||||
"sm": (256, ".thumb_256.jpg"),
|
|
||||||
"md": (748, ".thumb_748.jpg"),
|
|
||||||
"lg": (1024, ".thumb_1024.jpg"),
|
|
||||||
"xl": (2048, ".thumb_2048.jpg"),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def get_thumbnail_file(source_path, size_key):
|
|
||||||
"""Generates and retrieves the thumbnail file."""
|
|
||||||
size, suffix = THUMBNAIL_SIZES.get(size_key, (None, ""))
|
|
||||||
if size:
|
|
||||||
thumbnail_file = get_thumbnail(source_path, str(size), upscale=False)
|
|
||||||
return os.path.abspath(
|
|
||||||
os.path.join(settings.MEDIA_ROOT, thumbnail_file.name)
|
|
||||||
), suffix
|
|
||||||
return None, ""
|
|
||||||
|
|
||||||
|
|
||||||
def serve_content_file(request, file_hash):
|
|
||||||
"""
|
|
||||||
View function to serve content files for download or inline viewing.
|
|
||||||
"""
|
|
||||||
|
|
||||||
download = request.GET.get("d") == "0"
|
|
||||||
thumbnail_key = request.GET.get("t")
|
|
||||||
|
|
||||||
try:
|
|
||||||
obj_file = get_object_or_404(PostFileModel, hash_blake3=file_hash)
|
|
||||||
file_name = obj_file.name.first().filename
|
|
||||||
file_type = obj_file.file_type
|
|
||||||
source_file = obj_file.file
|
|
||||||
|
|
||||||
# Use thumbnail if requested and file type is image
|
|
||||||
if thumbnail_key and file_type != "image":
|
|
||||||
source_file = obj_file.thumbnail
|
|
||||||
|
|
||||||
# Retrieve the requested thumbnail file if applicable
|
|
||||||
if thumbnail_key in THUMBNAIL_SIZES:
|
|
||||||
thumbnail_path, suffix = get_thumbnail_file(source_file.path, thumbnail_key)
|
|
||||||
if thumbnail_path:
|
|
||||||
file_name += suffix
|
|
||||||
file = open(thumbnail_path, "rb")
|
|
||||||
else:
|
|
||||||
file = source_file.file
|
|
||||||
else:
|
|
||||||
file = source_file.file
|
|
||||||
|
|
||||||
response = FileResponse(file)
|
|
||||||
disposition_type = "attachment" if download else "inline"
|
|
||||||
response["Content-Disposition"] = f'{disposition_type}; filename="{file_name}"'
|
|
||||||
|
|
||||||
return response
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error serving file: {e}")
|
|
||||||
return HttpResponse("File not found", status=404)
|
|
Loading…
Add table
Reference in a new issue