Skip to content

Instantly share code, notes, and snippets.

@abesmon
Created May 12, 2025 09:51
Show Gist options
  • Save abesmon/40dde63b8c0199927d6ba4d464d8ea32 to your computer and use it in GitHub Desktop.
Save abesmon/40dde63b8c0199927d6ba4d464d8ea32 to your computer and use it in GitHub Desktop.
# Image Uploader Скрипт для загрузки изображений на ImgBB и FreeImage. Использует только стандартную библиотеку Python. ## Использование ```bash python image_uploader.py путь_к_изображению --service [imgbb|freeimage] [--expiration время_в_секундах] ``` Требуется файл `.env` с API ключами: ```env IMG_BB_API_KEY=ваш_ключ FREEIMAGE_API_KEY=ваш_ключ…
import os
import base64
import json
import urllib.request
import urllib.parse
import urllib.error
import argparse
import logging
from pathlib import Path
from typing import Optional, Dict, Any
# Настройка логирования
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
def load_env_file(env_path: str = ".env") -> None:
"""
Загружает переменные окружения из файла .env
Args:
env_path: Путь к файлу .env (по умолчанию ".env")
"""
try:
if not os.path.exists(env_path):
logger.warning(f"Файл {env_path} не найден")
return
with open(env_path, 'r', encoding='utf-8') as file:
for line in file:
line = line.strip()
if not line or line.startswith('#'):
continue
try:
key, value = line.split('=', 1)
key = key.strip()
value = value.strip()
# Удаляем кавычки, если они есть
if (value.startswith('"') and value.endswith('"')) or \
(value.startswith("'") and value.endswith("'")):
value = value[1:-1]
os.environ[key] = value
except ValueError:
logger.warning(f"Пропущена некорректная строка в {env_path}: {line}")
except Exception as e:
logger.error(f"Ошибка при загрузке {env_path}: {e}")
# Загрузка переменных окружения
load_env_file()
# Получение API ключей из переменных окружения
IMG_BB_API_KEY = os.getenv("IMG_BB_API_KEY")
FREEIMAGE_API_KEY = os.getenv("FREEIMAGE_API_KEY")
class ImageUploadError(Exception):
"""Базовый класс для ошибок загрузки изображений"""
pass
def read_image_file(image_path: str) -> bytes:
"""
Читает файл изображения и возвращает его содержимое в виде байтов.
Args:
image_path: Путь к файлу изображения
Returns:
bytes: Содержимое файла в виде байтов
Raises:
ImageUploadError: Если файл не существует или не может быть прочитан
"""
try:
with open(image_path, "rb") as file:
return file.read()
except Exception as e:
raise ImageUploadError(f"Ошибка при чтении файла: {e}")
def upload_image(
image_path: str,
service: str,
expiration: int = 180
) -> Optional[str]:
"""
Загружает изображение на выбранный сервис хостинга.
Args:
image_path: Путь к файлу изображения
service: Сервис хостинга ('imgbb' или 'freeimage')
expiration: Время жизни изображения в секундах
Returns:
Optional[str]: URL загруженного изображения или None в случае ошибки
"""
try:
image_data = read_image_file(image_path)
encoded_image = base64.b64encode(image_data)
if service == "imgbb":
if not IMG_BB_API_KEY:
raise ImageUploadError("IMG_BB_API_KEY не установлен")
return upload_to_imgbb(encoded_image, expiration)
elif service == "freeimage":
if not FREEIMAGE_API_KEY:
raise ImageUploadError("FREEIMAGE_API_KEY не установлен")
return upload_to_freeimage(encoded_image, expiration)
else:
raise ImageUploadError(f"Неизвестный сервис: {service}")
except ImageUploadError as e:
logger.error(str(e))
return None
except Exception as e:
logger.error(f"Неожиданная ошибка: {e}")
return None
def upload_to_imgbb(image_data: bytes, expiration: int) -> Optional[str]:
"""Загружает изображение на ImgBB"""
url = "https://api.imgbb.com/1/upload"
payload = {
"key": IMG_BB_API_KEY,
"image": image_data,
"expiration": expiration
}
try:
data = urllib.parse.urlencode(payload).encode('utf-8')
req = urllib.request.Request(url, data=data, method='POST')
with urllib.request.urlopen(req) as response:
result = json.loads(response.read().decode('utf-8'))
return result["data"]["url"]
except urllib.error.HTTPError as e:
raise ImageUploadError(f"Ошибка загрузки на ImgBB: {e.code}, {e.read().decode('utf-8')}")
except urllib.error.URLError as e:
raise ImageUploadError(f"Ошибка сети при загрузке на ImgBB: {e.reason}")
def upload_to_freeimage(image_data: bytes, expiration: int) -> Optional[str]:
"""Загружает изображение на FreeImage"""
url = "https://freeimage.host/api/1/upload"
payload = {
"key": FREEIMAGE_API_KEY,
"source": image_data,
"expiration": expiration
}
try:
data = urllib.parse.urlencode(payload).encode('utf-8')
req = urllib.request.Request(url, data=data, method='POST')
with urllib.request.urlopen(req) as response:
result = json.loads(response.read().decode('utf-8'))
return result["image"]["url"]
except urllib.error.HTTPError as e:
raise ImageUploadError(f"Ошибка загрузки на FreeImage: {e.code}, {e.read().decode('utf-8')}")
except urllib.error.URLError as e:
raise ImageUploadError(f"Ошибка сети при загрузке на FreeImage: {e.reason}")
def validate_image_path(image_path: str) -> bool:
"""Проверяет существование и доступность файла изображения"""
path = Path(image_path)
return path.is_file() and path.stat().st_size > 0
def main() -> None:
parser = argparse.ArgumentParser(description="Загрузка изображения на сервис хостинга")
parser.add_argument("image_path", help="Путь к файлу изображения")
parser.add_argument(
"--service",
choices=["imgbb", "freeimage"],
required=True,
help="Сервис хостинга изображений"
)
parser.add_argument(
"--expiration",
type=int,
default=180,
help="Время жизни изображения в секундах (по умолчанию: 180)"
)
args = parser.parse_args()
if not validate_image_path(args.image_path):
logger.error("Ошибка: Файл не существует или пуст")
return
url = upload_image(args.image_path, args.service, args.expiration)
if url:
logger.info(f"Изображение успешно загружено: {url}")
else:
logger.error("Не удалось загрузить изображение")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment