Media Flow (GraphQL + S3/MinIO)
Єдина схема завантаження/відображення медіа для accounts, company, taskManager, tg_client.
1. Як підключити у будь-яку GraphQL schema
from utils.graphql.service.media_mutations import (
CreateMediaUploadUrl,
GetMediaDisplayUrl,
)
class Mutation(graphene.ObjectType):
create_media_upload_url = CreateMediaUploadUrl.Field()
get_media_display_url = GetMediaDisplayUrl.Field()
Для аватара користувача окремо є CreateAvatarUploadUrl.
2. Запити для завантаження
2.1 Отримати presigned PUT URL
mutation CreateMediaUploadUrl {
createMediaUploadUrl(
source: "avatar",
filename: "photo.jpg",
contentType: "image/jpeg",
expiresIn: 600
) {
uploadUrl
key
expiresIn
}
}
Результат: uploadUrl (тимчасовий), key (постійний шлях об'єкта).
2.2 Завантажити файл
PUT {uploadUrl}
Body: binary file
Для presigned URL використовуйте
Не додавайте
binary body (не form-data).Не додавайте
Authorization header.
3. Запис key у модель
Після успішного PUT зберігайте у вашому полі саме key, а не повний URL.
Наприклад для ImageField:
item.image = "avatar/1/71ccc17ecd7040de9e73d0c41eecaf25.jpg"
4. Отримати URL для відображення
4.1 Згенерувати/оновити presigned GET URL
mutation GetMediaDisplayUrl {
getMediaDisplayUrl(
key: "avatar/1/71ccc17ecd7040de9e73d0c41eecaf25.jpg",
currentUrl: null,
expiresIn: 259200
) {
url
key
refreshed
expiresIn
}
}
refreshed=true означає, що URL був згенерований заново (старий прострочений/відсутній).
5. Повний фронтенд flow
async function uploadAndSaveImage({ file, source, saveMutation }) {
const { uploadUrl, key } = await gqlCreateMediaUploadUrl({
source,
filename: file.name,
contentType: file.type || "application/octet-stream",
expiresIn: 600,
});
const putResp = await fetch(uploadUrl, {
method: "PUT",
body: file,
});
if (!putResp.ok) throw new Error(`Upload failed: ${putResp.status}`);
await saveMutation({ imageKey: key }); // збереження key у вашій моделі
return key;
}
async function resolveImageUrl(key, currentUrl) {
const { url } = await gqlGetMediaDisplayUrl({
key,
currentUrl,
expiresIn: 259200,
});
return url;
}
6. Типові помилки
SignatureDoesNotMatch: URL змінений, не тойContent-Type, або запит не binary.Invalid Request (multiple authentication types): доданий зайвийAuthorizationheader.413 Request Entity Too Large: ліміт proxy перед S3/MinIO; збільшитиclient_max_body_size.