Skip to main content

Reusable Django app for resumable parallel chunked file uploads

Project description

Frago

Frago is a reusable Django app that provides a secure, resumable, and parallel chunked file upload system. It supports large file uploads with resumable support, duplicate chunk detection, checksum verification, and customizable signal hooks — ideal for drone videos, media applications, and IoT devices.


🚀 Features

  • Parallel & resumable chunked uploads
  • Upload tracking via database (start, end, size per chunk)
  • Checksum verification (MD5 or other algorithms)
  • Duplicate chunk detection
  • Upload expiration support
  • Extensible via Django signals
  • Pluggable authentication (user/device/anonymous)
  • Local file storage (pluggable)

📦 Installation

pip install frago

Add frago to your Django INSTALLED_APPS:
    # settings.py
    INSTALLED_APPS = [
        ...
        "frago",
    ]

Finally, run the following commands to create and apply the migrations:

    python manage.py makemigrations frago
    
    python manage.py migrate
    

⚙️ Configuration (optional)

you can confic these in settings.py

#🧠 How the uploader identifies the upload session

CHUNKED_UPLOADER_IDENTIFIER_MODE = "user"
 Options: "anonymous" (default), "user"
 You can override get_identifier() for custom logic.

# 📂 Where uploaded chunks are stored temporarily
CHUNKED_UPLOADER_CHUNK_UPLOAD_PATH = "chunked_uploads/"

# 📂 Where final assembled files go
CHUNKED_UPLOADER_ASSEMBLED_PATH = "assembled_videos/"

# 🔐 Hash type for file integrity checks
CHUNKED_UPLOADER_CHECKSUM_TYPE = "md5"
 Any hashlib-supported algorithm (e.g., "sha256")

# ✅ Whether to perform checksum verification
CHUNKED_UPLOADER_DO_CHECKSUM = True

# ⏱️ Chunk expiration time (used for cleanup jobs)
CHUNKED_UPLOADER_EXPIRATION = timedelta(days=1)

# 📦 Read buffer size during assembly
CHUNKED_UPLOADER_ASSEMBLE_READ_SIZE = 8 * 1024 * 1024  # 8MB

# 🧱 Custom chunk model path (if overriding)
CHUNKED_UPLOADER_CHUNK_MODEL = "frago.ChunkedUploadChunk"

# 🧩 Custom upload model path (if overriding)
CHUNKED_UPLOADER_UPLOAD_MODEL = "frago.ChunkUpload"

🧩 API Usage

1. Start a new upload
        POST /upload/
        Request:
        {
        "filename": "video.mp4",
        "total_size": 104857600
        }

        Response:
        {
        "status": true,
        "upload_id": "uuid4-string",
        "message": "Upload started for video.mp4"
        }
2. Upload a chunk
        PUT /upload/{upload_id}/

        Headers:
            Content-Range: bytes 0-1048575/104857600

        Body: multipart/form-data with key file and binary chunk data.
3. Finalize upload
        POST /upload/{upload_id}/

        Request
        {
        "checksum": "d41d8cd98f00b204e9800998ecf8427e",
        "checksum_algo": "md5"
        }

        Response
        {
        "status": true,
        "upload_id": "uuid4-string",
        "message": "Upload completed for video.mp4"
        }
4. Get upload status (only for authenticated users)
        GET /upload/{upload_id}/

        Or list uploads by current user/device:
        GET /upload/

🧠 Models

ChunkUpload

    Tracks a single upload:

        upload_id: UUID

        filename, total_size

        status: in_progress, complete, expired

        created_at, completed_at

ChunkedUploadChunk

    Tracks individual uploaded chunks:

        start, end, size

        Foreign key to ChunkUpload

🧰 Signals

Frago emits the following Django signals:
Signal	Triggered When
    upload_started : New upload is created
    chunk_received	: A chunk is received and saved
    upload_completed : All chunks assembled, upload finalized
    checksum_failed : Checksum mismatch during finalization

To extend behavior, connect signal handlers:
        from frago.signals import upload_started
        def my_handler(sender, upload, **kwargs):
            print("Upload started:", upload.filename)

        upload_started.connect(my_handler)

🧪 Usage Example

Add to your urls.py (if you are using with out any overides):
        path('api/',include('frago.urls')),

🧼 Cleanup:

Uploads expire after a period (EXPIRATION) and are marked expired.
Chunks and their metadata are deleted automatically on completion.

OVERIDING

This is an example how you can overide and set your custome logic

🔒 Custom Authentication Example

By default, the view allows anonymous access. You should subclass the view and add authentication:

    from rest_framework.permissions import IsAuthenticated
    from rest_framework_simplejwt.authentication import JWTAuthentication
    from frago.views import ParallelChunkedUploadView

    class SecureUploadView(ParallelChunkedUploadView):
        authentication_classes = [JWTAuthentication]
        permission_classes = [IsAuthenticated]

🧠 Overriding the Identifier

    class MyView(ParallelChunkedUploadView):
        authentication_classes = [CustomAuthentication]
        
        identifier_field = 'device'

        def get_identifier(self, request):
            if self.IDENTIFIER_MODE == 'device':
                
                device_id = request.user

                if not device_id:
                    raise PermissionDenied("Device ID not found.")

                try:
                    device = Devices.objects.get(inventory_id=device_id)
                    print('device',device.id)
                    return device  # or another unique identifier
                except Devices.DoesNotExist:
                    raise PermissionDenied("Invalid Device ID.")

            # fallback to parent logic
            return super().get_identifier(request)
        

Update your urls.py:

    urlpatterns = [
        path('upload/',MyView.as_view()),
        path('upload/<uuid:pk>/', MyView.as_view()),
    ]

🔧 Custom Upload Model Example

    class Mychunkupload(AbstractChunkUpload):
        device = models.ForeignKey(Devices,on_delete=models.CASCADE)
        

        def assembled_path(self):
            try:
                device = Devices.objects.get(inventory_id=self.device_id)
                base_path = device.videos or settings.ASSEMBLED_PATH
            except Exception as e:
                base_path = settings.ASSEMBLED_PATH
            return os.path.join(base_path, f'{self.filename}')
        

Register it in settings:

    CHUNKED_UPLOADER_UPLOAD_MODEL = 'fragotest.Mychunkupload'

and specify the identifier field in the view

    identifier_field = 'device'

🔗 Uploader Client

Use the official uploader client:
👉 frago-client

📄 License

This project is licensed under the MIT License.

🤝 Contributing

Pull requests are welcome! 
Please open an issue first to discuss your idea.
Make sure to add tests for any new features or logic changes.

🙌 Author

Built with ❤️ to support scalable file upload workflows in Django.
Let me know if you want:
    Markdown preview badges
    GitHub Actions/test coverage
    Python client script (httpx/requests)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

frago-0.1.4.tar.gz (14.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

frago-0.1.4-py3-none-any.whl (13.7 kB view details)

Uploaded Python 3

File details

Details for the file frago-0.1.4.tar.gz.

File metadata

  • Download URL: frago-0.1.4.tar.gz
  • Upload date:
  • Size: 14.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for frago-0.1.4.tar.gz
Algorithm Hash digest
SHA256 327faee6b65a94c96ca5ec79a6c71f382c088012c10624c9dead82eb0f789b2c
MD5 d9f0ab956d16af7a9703c6805bb7ee01
BLAKE2b-256 83f03b4c58bb565b9b4323efe7629cbaf0d3322f4933a37f409df2fef9339a26

See more details on using hashes here.

Provenance

The following attestation bundles were made for frago-0.1.4.tar.gz:

Publisher: publish.yml on Albinm123/frago

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file frago-0.1.4-py3-none-any.whl.

File metadata

  • Download URL: frago-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 13.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for frago-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 967c9fca5686d057e4dd33501a15aa39bbe1a94a829786119275eb69cacadec1
MD5 f82559c895ace92f151c477bc140f3e8
BLAKE2b-256 4de943600c801afee8c7682d33bfa0d96a478b84002d2d71fc593d0fe500fd24

See more details on using hashes here.

Provenance

The following attestation bundles were made for frago-0.1.4-py3-none-any.whl:

Publisher: publish.yml on Albinm123/frago

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page