DJANGO 5 分钟 4,923 字

七牛云密钥(放 .env 也行)

0阅读

下面给你一个「零鉴权、最小可运行」的 Django + DRF + 七牛云直传示例。
只有 3 个文件需要改动/新增,复制即可跑通:


  1. 安装依赖
pip install django djangorestframework qiniu python-dotenv

  1. settings.py(只保留关键行)
INSTALLED_APPS = [
    'django.contrib.staticfiles',
    'rest_framework',
    'uploader',  # 新建 app,名字随意
]

QINIU_ACCESS_KEY = '你的AK'
QINIU_SECRET_KEY = '你的SK'
QINIU_BUCKET_NAME = '你的空间名'
QINIU_BUCKET_DOMAIN = 'https://你的域名/'  # 结尾带 /

  1. 新建 uploader/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from qiniu import Auth
from django.conf import settings
import uuid, os


class UploadTokenView(APIView):
    """POST /upload/token/  返回 uptoken + key + url"""

    def post(self, request):
        filename = request.data.get('filename', 'unnamed')
        suffix = os.path.splitext(filename)[-1]
        key = f"demo/{uuid.uuid4().hex}{suffix}"
        token = Auth(
            settings.QINIU_ACCESS_KEY,
            settings.QINIU_SECRET_KEY
        ).upload_token(settings.QINIU_BUCKET_NAME, key)
        return Response({
            'uptoken': token,
            'key': key,
            'url': settings.QINIU_BUCKET_DOMAIN + key
        })

  1. uploader/urls.py
from django.urls import path
from .views import UploadTokenView

urlpatterns = [
    path('upload/token/', UploadTokenView.as_view()),
]

  1. 根 urls.py
from django.urls import include, path

urlpatterns = [
    path('', include('uploader.urls')),
]

  1. 运行
python manage.py migrate   # 建表,虽然这里没模型
python manage.py runserver

  1. 测试(curl 即可)
curl -X POST http://localhost:8000/upload/token/ \
     -H "Content-Type: application/json" \
     -d '{"filename":"hello.txt"}'

返回示例

{
  "uptoken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "key": "demo/a3f42b1c8e5d4f7a89b3c1d4e5f6a7b8.txt",
  "url": "https://你的域名/demo/a3f42b1c8e5d4f7a89b3c1d4e5f6a7b8.txt"
}

把 uptoken、key 和文件一起 POST 到 https://upload.qiniup.com 就完成上传——全程不需要任何登录/鉴权。

  1. 用 curl 快速验证(本地文件版)
# 1. 先拿 token(刚才的接口)
RESP=$(curl -s -X POST http://localhost:8000/upload/token/ \
            -H "Content-Type: application/json" \
            -d '{"filename":"hello.txt"}')
TOKEN=$(echo $RESP | jq -r .uptoken)
KEY=$(echo $RESP | jq -r .key)

# 2. 真正上传文件(关键步骤)
curl -F "token=$TOKEN" \
     -F "key=$KEY" \
     -F "file=@/path/to/hello.txt" \
     https://upload.qiniup.com

# 返回 {"hash":"FqUqya...","key":"demo/xxx.txt"} 即成功

  1. 前端浏览器直传最小示例
<input type="file" id="f">
<button onclick="up()">上传</button>

<script>
    async function up() {
        const file = document.getElementById('f').files[0];
        // 1. 拿 uptoken
        const {uptoken, key, url} = await fetch('http://localhost:8000/upload/token/', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({filename: file.name})
        }).then(r => r.json());

        // 2. 构造表单直传七牛
        const form = new FormData();
        form.append('token', uptoken);
        form.append('key', key);
        form.append('file', file);

        await fetch('https://upload.qiniup.com', {method: 'POST', body: form});
        alert('上传完成!七牛外链:' + url);
    }
</script>

  1. 常见坑检查清单
    现象 原因
    200 但空间没文件 只拿了 token,没发第二步
    返回 bad token token 里 bucket 与空间不符 / AK/SK 填错
    返回 file is required form 里字段名必须叫 file
    返回 no such domain 上传域名用错,参考控制台「上传地址」

一句话总结
/upload/token/ ≠ 上传完成,它只是「通行证」;
再发一次 POST https://upload.qiniup.com 带上 token、key、file 才算真正上传,成功后七牛控制台立刻能看到文件。

补齐 2 个常用接口:

  1. 查看(列举)桶内文件 – 支持前缀、分页
  2. 删除指定文件 – 支持单条/批量

全部用 Class-based,复制即可跑通。


  1. 安装依赖(已装可跳过)
pip install qiniu

  1. uploader/views.py 追加
from rest_framework.views import APIView
from rest_framework.response import Response
from qiniu import Auth, BucketManager
from django.conf import settings

auth = Auth(settings.QINIU_ACCESS_KEY, settings.QINIU_SECRET_KEY)
bm = BucketManager(auth)


class FileListView(APIView):
    """
    GET /files/?prefix=demo/&limit=10&marker=
    列举空间文件,支持前缀、分页
    """

    def get(self, request):
        bucket = settings.QINIU_BUCKET_NAME
        prefix = request.query_params.get('prefix', '')
        limit = int(request.query_params.get('limit', 20))
        marker = request.query_params.get('marker', None)

        ret, eof, info = bm.list(bucket, prefix=prefix, limit=limit, marker=marker)
        if ret is None:
            return Response({'error': info.text}, status=400)
        return Response({
            'items': ret['items'],  # 文件数组
            'marker': ret.get('marker'),  # 翻页游标
            'eof': eof  # 是否已读完
        })


class FileDeleteView(APIView):
    """
    POST /delete/   {"key": "demo/a3f42b.txt"}
    删除单个文件;可改成批量
    """

    def post(self, request):
        key = request.data.get('key')
        if not key:
            return Response({'error': 'key 必填'}, status=400)

        ret, info = bm.delete(settings.QINIU_BUCKET_NAME, key)
        if ret is None or info.status_code != 200:
            return Response({'error': info.text}, status=info.status_code)
        return Response({'message': '已删除'})

  1. uploader/urls.py 追加
urlpatterns += [
    path('files/', FileListView.as_view(), name='file-list'),
    path('delete/', FileDeleteView.as_view(), name='file-delete'),
]

  1. 快速测试(curl)
# 列举
curl "http://localhost:8000/files/?prefix=demo/&limit=5"

# 删除
curl -X POST http://localhost:8000/delete/ \
     -H "Content-Type: application/json" \
     -d '{"key":"demo/a3f42b1c8e5d4f7a89b3c1d4e5f6a7b8.txt"}'

  1. 接口说明
    接口 方法 参数 返回
    /files/ GET prefix 路径前缀
    limit ≤1000
    marker 翻页游标
    {items:[{key,hash,fsize,putTime,mimeType,...}],marker,eof}
    /delete/ POST {"key":"空间内文件名"} {"message":"已删除"}

批量删除可循环调用,或用七牛 batch 接口一次传 1000 条 。

至此,「上传-查看-删除」闭环完成,祝你使用愉快!