Remove chunked upload code (no longer needed with direct bypass)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8220ad5d3b
commit
9214aed499
|
|
@ -10,8 +10,6 @@ from portal.views_shared_space import (
|
||||||
SharedSpaceLogoutView,
|
SharedSpaceLogoutView,
|
||||||
SharedSpaceUploadAPIView,
|
SharedSpaceUploadAPIView,
|
||||||
SharedSpaceFileListView,
|
SharedSpaceFileListView,
|
||||||
ChunkedUploadInitView,
|
|
||||||
ChunkedUploadChunkView,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -20,7 +18,5 @@ urlpatterns = [
|
||||||
path('login/', SharedSpaceLoginView.as_view(), name='shared_space_login'),
|
path('login/', SharedSpaceLoginView.as_view(), name='shared_space_login'),
|
||||||
path('logout/', SharedSpaceLogoutView.as_view(), name='shared_space_logout'),
|
path('logout/', SharedSpaceLogoutView.as_view(), name='shared_space_logout'),
|
||||||
path('api/upload/', SharedSpaceUploadAPIView.as_view(), name='shared_space_upload'),
|
path('api/upload/', SharedSpaceUploadAPIView.as_view(), name='shared_space_upload'),
|
||||||
path('api/upload/init/', ChunkedUploadInitView.as_view(), name='chunked_upload_init'),
|
|
||||||
path('api/upload/chunk/', ChunkedUploadChunkView.as_view(), name='chunked_upload_chunk'),
|
|
||||||
path('files/', SharedSpaceFileListView.as_view(), name='shared_space_files'),
|
path('files/', SharedSpaceFileListView.as_view(), name='shared_space_files'),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,6 @@ Each topic is accessible via a subdomain (e.g., cofi.rfiles.online).
|
||||||
Anyone can view files and upload new ones - no password required.
|
Anyone can view files and upload new ones - no password required.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.core.files.base import File
|
|
||||||
from django.shortcuts import render, redirect, get_object_or_404
|
from django.shortcuts import render, redirect, get_object_or_404
|
||||||
from django.http import JsonResponse, Http404
|
from django.http import JsonResponse, Http404
|
||||||
from django.views import View
|
from django.views import View
|
||||||
|
|
@ -18,8 +13,6 @@ from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from files.models import SharedSpace, MediaFile, PublicShare
|
from files.models import SharedSpace, MediaFile, PublicShare
|
||||||
|
|
||||||
CHUNK_UPLOAD_DIR = os.path.join(settings.MEDIA_ROOT, 'chunks')
|
|
||||||
|
|
||||||
|
|
||||||
def get_topic_or_404(request):
|
def get_topic_or_404(request):
|
||||||
"""Get the topic (shared space) from the request's subdomain slug."""
|
"""Get the topic (shared space) from the request's subdomain slug."""
|
||||||
|
|
@ -224,171 +217,6 @@ class DirectUploadAPIView(View):
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(csrf_exempt, name='dispatch')
|
|
||||||
class ChunkedUploadInitView(View):
|
|
||||||
"""Initialize a chunked upload session."""
|
|
||||||
|
|
||||||
def post(self, request):
|
|
||||||
space = get_topic_or_404(request)
|
|
||||||
filename = request.POST.get('filename', '')
|
|
||||||
total_size = int(request.POST.get('total_size', 0))
|
|
||||||
total_chunks = int(request.POST.get('total_chunks', 0))
|
|
||||||
mime_type = request.POST.get('mime_type', 'application/octet-stream')
|
|
||||||
action = request.POST.get('action', '')
|
|
||||||
|
|
||||||
if not filename or total_chunks < 1:
|
|
||||||
return JsonResponse({'error': 'Invalid parameters'}, status=400)
|
|
||||||
|
|
||||||
if space.max_file_size_mb > 0:
|
|
||||||
max_size_bytes = space.max_file_size_mb * 1024 * 1024
|
|
||||||
if total_size > max_size_bytes:
|
|
||||||
return JsonResponse({
|
|
||||||
'error': f'File too large. Maximum size is {space.max_file_size_mb}MB'
|
|
||||||
}, status=400)
|
|
||||||
|
|
||||||
# Check for duplicate
|
|
||||||
existing = MediaFile.objects.filter(
|
|
||||||
shared_space=space,
|
|
||||||
original_filename=filename,
|
|
||||||
).first()
|
|
||||||
|
|
||||||
if existing and action != 'overwrite':
|
|
||||||
existing_share = existing.public_shares.filter(is_active=True).first()
|
|
||||||
return JsonResponse({
|
|
||||||
'duplicate': True,
|
|
||||||
'existing_file': {
|
|
||||||
'id': str(existing.id),
|
|
||||||
'title': existing.title,
|
|
||||||
'filename': existing.original_filename,
|
|
||||||
'size': existing.file_size,
|
|
||||||
'share_url': existing_share.get_public_url() if existing_share else None,
|
|
||||||
},
|
|
||||||
}, status=409)
|
|
||||||
|
|
||||||
upload_id = str(uuid.uuid4())
|
|
||||||
chunk_dir = os.path.join(CHUNK_UPLOAD_DIR, upload_id)
|
|
||||||
os.makedirs(chunk_dir, exist_ok=True)
|
|
||||||
|
|
||||||
# Store metadata
|
|
||||||
import json
|
|
||||||
meta = {
|
|
||||||
'filename': filename,
|
|
||||||
'total_size': total_size,
|
|
||||||
'total_chunks': total_chunks,
|
|
||||||
'mime_type': mime_type,
|
|
||||||
'space_id': str(space.id),
|
|
||||||
'action': action,
|
|
||||||
}
|
|
||||||
with open(os.path.join(chunk_dir, 'meta.json'), 'w') as f:
|
|
||||||
json.dump(meta, f)
|
|
||||||
|
|
||||||
return JsonResponse({'upload_id': upload_id})
|
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(csrf_exempt, name='dispatch')
|
|
||||||
class ChunkedUploadChunkView(View):
|
|
||||||
"""Receive a single chunk and finalize when all chunks are received."""
|
|
||||||
|
|
||||||
def post(self, request):
|
|
||||||
space = get_topic_or_404(request)
|
|
||||||
upload_id = request.POST.get('upload_id', '')
|
|
||||||
chunk_index = int(request.POST.get('chunk_index', -1))
|
|
||||||
chunk_file = request.FILES.get('chunk')
|
|
||||||
|
|
||||||
if not upload_id or chunk_index < 0 or not chunk_file:
|
|
||||||
return JsonResponse({'error': 'Invalid parameters'}, status=400)
|
|
||||||
|
|
||||||
chunk_dir = os.path.join(CHUNK_UPLOAD_DIR, upload_id)
|
|
||||||
meta_path = os.path.join(chunk_dir, 'meta.json')
|
|
||||||
if not os.path.isdir(chunk_dir) or not os.path.exists(meta_path):
|
|
||||||
return JsonResponse({'error': 'Invalid upload_id'}, status=400)
|
|
||||||
|
|
||||||
import json
|
|
||||||
with open(meta_path) as f:
|
|
||||||
meta = json.load(f)
|
|
||||||
|
|
||||||
# Verify this chunk belongs to this space
|
|
||||||
if meta['space_id'] != str(space.id):
|
|
||||||
return JsonResponse({'error': 'Space mismatch'}, status=403)
|
|
||||||
|
|
||||||
# Write chunk to disk
|
|
||||||
chunk_path = os.path.join(chunk_dir, f'{chunk_index:06d}')
|
|
||||||
with open(chunk_path, 'wb') as f:
|
|
||||||
for part in chunk_file.chunks():
|
|
||||||
f.write(part)
|
|
||||||
|
|
||||||
# Check if all chunks received
|
|
||||||
received = len([
|
|
||||||
n for n in os.listdir(chunk_dir)
|
|
||||||
if n != 'meta.json'
|
|
||||||
])
|
|
||||||
|
|
||||||
if received < meta['total_chunks']:
|
|
||||||
return JsonResponse({
|
|
||||||
'received': received,
|
|
||||||
'total': meta['total_chunks'],
|
|
||||||
})
|
|
||||||
|
|
||||||
# All chunks received — assemble the file
|
|
||||||
assembled_path = os.path.join(chunk_dir, 'assembled')
|
|
||||||
with open(assembled_path, 'wb') as out:
|
|
||||||
for i in range(meta['total_chunks']):
|
|
||||||
part_path = os.path.join(chunk_dir, f'{i:06d}')
|
|
||||||
with open(part_path, 'rb') as part:
|
|
||||||
while True:
|
|
||||||
buf = part.read(8192)
|
|
||||||
if not buf:
|
|
||||||
break
|
|
||||||
out.write(buf)
|
|
||||||
|
|
||||||
# Handle overwrite
|
|
||||||
if meta['action'] == 'overwrite':
|
|
||||||
existing = MediaFile.objects.filter(
|
|
||||||
shared_space=space,
|
|
||||||
original_filename=meta['filename'],
|
|
||||||
).first()
|
|
||||||
if existing:
|
|
||||||
existing.file.delete(save=False)
|
|
||||||
existing.delete()
|
|
||||||
|
|
||||||
# Create MediaFile from assembled file
|
|
||||||
with open(assembled_path, 'rb') as f:
|
|
||||||
django_file = File(f, name=meta['filename'])
|
|
||||||
media_file = MediaFile.objects.create(
|
|
||||||
file=django_file,
|
|
||||||
original_filename=meta['filename'],
|
|
||||||
title=meta['filename'],
|
|
||||||
mime_type=meta['mime_type'],
|
|
||||||
uploaded_by=request.user if request.user.is_authenticated else None,
|
|
||||||
shared_space=space,
|
|
||||||
)
|
|
||||||
|
|
||||||
share = PublicShare.objects.create(
|
|
||||||
media_file=media_file,
|
|
||||||
created_by=request.user if request.user.is_authenticated else None,
|
|
||||||
note=f'Uploaded to topic: {space.slug}',
|
|
||||||
)
|
|
||||||
|
|
||||||
# Cleanup chunk directory
|
|
||||||
import shutil
|
|
||||||
shutil.rmtree(chunk_dir, ignore_errors=True)
|
|
||||||
|
|
||||||
return JsonResponse({
|
|
||||||
'success': True,
|
|
||||||
'file': {
|
|
||||||
'id': str(media_file.id),
|
|
||||||
'title': media_file.title,
|
|
||||||
'filename': media_file.original_filename,
|
|
||||||
'size': media_file.file_size,
|
|
||||||
'mime_type': media_file.mime_type,
|
|
||||||
},
|
|
||||||
'share': {
|
|
||||||
'token': share.token,
|
|
||||||
'url': share.get_public_url(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
class SharedSpaceFileListView(View):
|
class SharedSpaceFileListView(View):
|
||||||
"""List all files in the topic."""
|
"""List all files in the topic."""
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue