MinIO on Django: Quick Intro and Popular Python Bindings
Table of Contents
Only for minio==6.0.2
and django-minio-storage==0.3.8
.
好吧,规定这个是因为 django-minio-storage
依赖里不写 minio
版本,而且 minio
在新版本中改动了 API。
Fuck you Amazon
但是其实要感谢 Amazon 的那个鬼签名机制我没自己写出来,否则我就不会想到去调库了(其实是俺的傻逼行为
使用 django-minio-storage
进行配置。
INSTALLED_APPS = [
...
'minio_storage',
...
]
# Use Minio as file storage backend
STATIC_ROOT = './static_files/'
DEFAULT_FILE_STORAGE = "minio_storage.storage.MinioMediaStorage"
MINIO_STORAGE_ENDPOINT = environ['MINIO_ADDRESS']
MINIO_STORAGE_ACCESS_KEY = environ['MINIO_ACCESS_KEY']
MINIO_STORAGE_SECRET_KEY = environ['MINIO_SECRET_KEY']
# 这个选项控制手动连接 MinIO 的时候使用的 HTTP Scheme
MINIO_STORAGE_USE_HTTPS = False
MINIO_STORAGE_MEDIA_OBJECT_METADATA = {"Cache-Control": "max-age=1000"}
MINIO_STORAGE_MEDIA_URL = environ['MINIO_ADDRESS']
MINIO_STORAGE_MEDIA_BUCKET_NAME = 'media-bucket'
# 这个选项控制 Django 如何使用存储:从 Django 中写文件时是否会自动创建对应的存储桶
MINIO_STORAGE_AUTO_CREATE_MEDIA_BUCKET = True
MINIO_STORAGE_STATIC_BUCKET_NAME = 'static-bucket'
MINIO_STORAGE_AUTO_CREATE_STATIC_BUCKET = True
在你的 Django 应用中测试:
from django.utils import timezone
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework.views import APIView
from django.core.files.base import ContentFile
from django.core.files.storage import default_storage
from django.core.files.storage import Storage
from django.core.files import File
class StatusView(APIView):
# Omit unrelated codes
def minio_connected(self):
try:
testfile_content = b'minio here'
path = default_storage.save('here/alive', ContentFile(testfile_content))
testfile_content_read = default_storage.open(path).read()
default_storage.delete(path)
return testfile_content == testfile_content_read
except Exception:
return False
直接使用 minio
生成 PUT 文件需要的 URL
import json
import datetime
from minio import Minio
from minio.error import ResponseError
from datetime import timedelta
from os import environ
from .settings import MINIO_STORAGE_MEDIA_BUCKET_NAME as DEFAULT_BUCKET
from .settings import MINIO_STORAGE_USE_HTTPS
import random
# minio client to use
# TODO: when deployed and access through remote machine,
# minio remote address should be changed to HTTP_HOST
# with corresponding information.
local_minio_client = Minio(
environ['MINIO_ADDRESS'],
access_key=environ['MINIO_ACCESS_KEY'],
secret_key=environ['MINIO_SECRET_KEY'],
secure=MINIO_STORAGE_USE_HTTPS,
)
# default timeout = 15 min
DEFAULT_TIMEOUT = timedelta(minutes=15)
# PREFIX DIR
PREFIX = "******"
file_token = f"{PREFIX}/{file_display_name}"
put_url = local_minio_client.presigned_url("PUT",
DEFAULT_BUCKET,
file_token,
expires=DEFAULT_TIMEOUT)
print(put_url)
生成 GET 文件所需要的 URL
# imports and minio client are omitted
file_token = "******"
result_url = local_minio_client.presigned_url("GET",
DEFAULT_BUCKET,
file_token,
expires=DEFAULT_FILE_URL_TIMEOUT)
print(result_url)
生成预定义的,限制上传文件大小的 POST URL;上传时需要自定义 form data
# imports and minio client are omitted
from minio import PostPolicy
# MAX SIZE (MB)
MAX_SIZE = 4 * 1024 * 1024
post_policy = PostPolicy()
# set bucket name location for uploads.
post_policy.set_bucket_name(DEFAULT_BUCKET)
# set key prefix for all incoming uploads.
file_token = "******"
post_policy.set_key_startswith(file_token)
# set content length for incoming uploads.
post_policy.set_content_length_range(0, MAX_SIZE)
# set expiry.
expires_date = datetime.utcnow() + DEFAULT_FILE_URL_TIMEOUT
post_policy.set_expires(expires_date)
url, signed_form_data = local_minio_client.presigned_post_policy(post_policy)
print(url)
print(signed_form_data)
上传时:
Content-Type: multipart/form-data; boundary=<calculated when request is sent>
- 将上一步的
signed_form_data
中的内容放入请求体。 - 将需要上传的文件放入请求体中的
file
栏。
(在启动时)修改存储桶策略,使得某路径下的文件能被公开访问
# imports and minio client are omitted
# predefined anonymous read-only avatar policy
predefined_avatar_policy = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {"AWS": "*"},
"Action": "s3:GetObject",
"Resource": f"arn:aws:s3:::{DEFAULT_BUCKET}/{PREFIX}/*",
},
],
}
local_minio_client.set_bucket_policy(DEFAULT_BUCKET, json.dumps(predefined_avatar_policy))