How File Upload Handling Works with Qiniu Cloud Storage in Mini‑Shop Server

Mini‑Shop Server delegates file uploads to a QiniuUploader class that deduplicates files by MD5 hash, streams them to Qiniu object storage via the official SDK, and persists metadata to a local database.

The allen7d/mini-shop-server repository implements a decoupled file upload system for its Flask-based e-commerce backend. When handling file upload handling with Qiniu cloud storage, the application uses a dedicated uploader class to stream multipart files directly to Qiniu (七牛云) while maintaining local records for deduplication and retrieval.

Architecture Overview

The upload pipeline is split between the API layer and the storage extension. The endpoint in app/api/cms/file.py receives FileStorage objects, while the heavy lifting is performed by QiniuUploader in app/extensions/file/qiniu_uploader.py. This separation allows the system to switch between cloud and local storage without changing route logic.

Step‑by‑Step Upload Flow

1. File Reception and Deduplication

When a multipart request hits the CMS file endpoint, the system instantiates a QiniuUploader (or falls back to LocalUploader). For each incoming file, the uploader computes an MD5 hash via _generate_md5.

The implementation checks app/models/file.py for existing records matching that hash. If found and the target folder differs, FileDao.copy_file in app/dao/file.py duplicates the database entry instead of re-uploading the binary. This prevents redundant storage costs and network traffic.

2. Streaming to Qiniu Object Storage

The save method in app/extensions/file/qiniu_uploader.py constructs an upload token using the access key, secret key, and bucket name defined in the Flask config under the QINIU section (typically set in config.ini or app/config/setting.py).

Using the official Qiniu Python SDK, the method calls put_data(token, None, file) to stream the file bytes directly to the cloud. On success (info.status_code == 200), the uploader builds the public URL by concatenating the configured domain with the key returned by Qiniu: domain + ret.get("key").

3. Database Record Creation

After a successful upload, the system creates a new File model record containing the original filename, a generated UUID filename, the full Qiniu URL as the storage path, file extension, size, MD5 hash, and the marker UrlFromEnum.NETWORK to indicate cloud-hosted assets. The API layer returns this metadata to the client as a serialized list of File objects.

Configuration Requirements

To enable cloud storage, the application expects the following keys in the Flask app config:

  • ACCESS_KEY: Qiniu account access key
  • SECRET_KEY: Qiniu account secret key
  • BUCKET_NAME: Target storage bucket
  • DOMAIN: Public domain for URL construction

These are typically loaded from config.ini or app/config/setting.py under the QINIU section.

Example API Usage

You can test the upload endpoint using cURL:

curl -X POST http://localhost:5000/api/v1/file/upload \
  -H "Authorization: Bearer <token>" \
  -F "file=@/path/to/image.jpg"

The server performs MD5 deduplication, uploads the binary to Qiniu via put_data, and returns JSON containing the file metadata and public URL (e.g., https://<your-domain>/<key>).

Summary

  • Deduplication by MD5: The QiniuUploader avoids redundant uploads by hashing files and checking against existing records in app/models/file.py.
  • Direct streaming: Files are sent to Qiniu using put_data from the official SDK, with authentication tokens built from config keys in the QINIU section.
  • Flexible storage: The design decouples upload logic from API routes via app/extensions/file/qiniu_uploader.py, enabling easy fallback to LocalUploader.
  • Metadata tracking: Each upload creates a database entry with UrlFromEnum.NETWORK to distinguish cloud assets from local files.

Frequently Asked Questions

How does Mini‑Shop Server prevent duplicate file uploads?

The system computes an MD5 hash of each file via _generate_md5 and queries the database for existing matches. If the same hash exists, it either reuses the existing record or copies it to a new folder using FileDao.copy_file, skipping the actual network upload to Qiniu.

What Qiniu SDK method is used to upload files?

The save method in app/extensions/file/qiniu_uploader.py uses put_data(token, None, file) from the official Qiniu Python SDK. This streams the file bytes directly to object storage and returns a public URL constructed from the configured domain and the key returned by Qiniu.

Where is the Qiniu configuration stored?

Configuration values including ACCESS_KEY, SECRET_KEY, BUCKET_NAME, and DOMAIN are stored in the Flask app config under the QINIU section, typically defined in config.ini or app/config/setting.py.

Can the system fall back to local storage?

Yes. The API endpoint in app/api/cms/file.py can instantiate either QiniuUploader or LocalUploader based on configuration or runtime conditions, allowing the same upload interface to work with both cloud and local filesystems without changing the route code.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →