Device
This commit is contained in:
141
backend/storage/storage_service.py
Normal file
141
backend/storage/storage_service.py
Normal file
@@ -0,0 +1,141 @@
|
||||
import re
|
||||
import uuid
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
from storage.s3_client import (
|
||||
get_s3_client,
|
||||
get_bucket_name,
|
||||
get_public_url,
|
||||
)
|
||||
from storage.validators import validate_image_file
|
||||
|
||||
|
||||
def slugify(text):
|
||||
"""
|
||||
Chuyển text thành dạng an toàn để dùng trong tên file.
|
||||
|
||||
Ví dụ:
|
||||
- "Router" -> "router"
|
||||
- "Core Switch 01" -> "core-switch-01"
|
||||
- "Thiết bị định tuyến" -> "thi-t-b-nh-tuy-n"
|
||||
|
||||
Lưu ý:
|
||||
Hàm này đơn giản, chưa xử lý tiếng Việt hoàn hảo.
|
||||
Nếu muốn slug tiếng Việt đẹp hơn, sau này có thể dùng thư viện python-slugify.
|
||||
"""
|
||||
|
||||
if text is None or text.strip() == "":
|
||||
return "file"
|
||||
|
||||
text = text.lower().strip()
|
||||
|
||||
# Thay toàn bộ ký tự không phải chữ/số bằng dấu gạch ngang.
|
||||
text = re.sub(r"[^a-z0-9]+", "-", text)
|
||||
|
||||
# Xóa dấu gạch ngang thừa ở đầu/cuối.
|
||||
text = text.strip("-")
|
||||
|
||||
return text or "file"
|
||||
|
||||
|
||||
def build_object_key(folder, business_name, extension):
|
||||
"""
|
||||
Tạo object key để lưu file trong bucket.
|
||||
|
||||
MinIO/S3 không có folder thật như filesystem.
|
||||
Folder ở đây chỉ là prefix trong object key.
|
||||
|
||||
Ví dụ:
|
||||
folder = "device-types"
|
||||
business_name = "Router"
|
||||
extension = "svg"
|
||||
|
||||
Kết quả:
|
||||
device-types/router-550e8400.svg
|
||||
|
||||
Dùng UUID để tránh trùng tên file.
|
||||
"""
|
||||
|
||||
safe_name = slugify(business_name)
|
||||
unique_id = uuid.uuid4()
|
||||
|
||||
return f"{folder}/{safe_name}-{unique_id}.{extension}"
|
||||
|
||||
|
||||
def upload_image(file, folder, business_name): # Hàm Upload dùng chung cho device_types và devices
|
||||
"""
|
||||
Upload một file ảnh lên MinIO/S3 và trả về public URL.
|
||||
|
||||
Tham số:
|
||||
- file: file lấy từ request.files
|
||||
- folder: nhóm lưu trữ, ví dụ "device-types" hoặc "devices"
|
||||
- business_name: tên nghiệp vụ để đặt tên file đẹp hơn
|
||||
Ví dụ:
|
||||
+ DeviceType name = Router
|
||||
+ Device name = Switch tầng 2
|
||||
|
||||
Flow:
|
||||
1. Validate file
|
||||
2. Tạo object key
|
||||
3. Upload file lên bucket
|
||||
4. Trả về URL public để lưu vào database
|
||||
"""
|
||||
|
||||
extension = validate_image_file(file)
|
||||
|
||||
object_key = build_object_key(
|
||||
folder=folder,
|
||||
business_name=business_name,
|
||||
extension=extension,
|
||||
)
|
||||
|
||||
s3_client = get_s3_client()
|
||||
bucket_name = get_bucket_name()
|
||||
public_url = get_public_url()
|
||||
|
||||
s3_client.upload_fileobj(
|
||||
file,
|
||||
bucket_name,
|
||||
object_key,
|
||||
ExtraArgs={
|
||||
"ContentType": file.content_type
|
||||
},
|
||||
)
|
||||
|
||||
return f"{public_url}/{bucket_name}/{object_key}"
|
||||
|
||||
|
||||
def upload_device_type_icon(file, device_type_name):
|
||||
"""
|
||||
Upload icon cho DeviceType.
|
||||
|
||||
File sẽ được lưu trong bucket với prefix:
|
||||
device-types/
|
||||
|
||||
Ví dụ URL:
|
||||
http://localhost:9000/ndms/device-types/router-uuid.svg
|
||||
"""
|
||||
|
||||
return upload_image( # Ở đoạn này có thể trả về một hàm luôn á ? Mà không cần phải gọi hàm đó vào bên trong và truyền tham số à ?
|
||||
file=file,
|
||||
folder="device-types",
|
||||
business_name=device_type_name,
|
||||
)
|
||||
|
||||
|
||||
def upload_device_avatar(file, device_name):
|
||||
"""
|
||||
Upload avatar cho Device.
|
||||
|
||||
File sẽ được lưu trong bucket với prefix:
|
||||
devices/
|
||||
|
||||
Ví dụ URL:
|
||||
http://localhost:9000/ndms/devices/switch-tang-2-uuid.png
|
||||
"""
|
||||
|
||||
return upload_image(
|
||||
file=file,
|
||||
folder="devices",
|
||||
business_name=device_name,
|
||||
)
|
||||
Reference in New Issue
Block a user