Реалізація переносу медіафайлів при міграції сайту
Медіафайли — зображення, документи, відео, архіви — нерідко складають десятки гігабайтів. Перенесення потребує не тільки копіювання файлів, але й оновлення всіх посилань у контенті.
Інвентаризація медіафайлів
# Зведка по розмірам та типах
find /var/www/uploads -type f | \
awk -F. '{print $NF}' | sort | uniq -c | sort -rn
# Загальний розмір
du -sh /var/www/uploads
# Файли без відповідності в БД (можливий мусор)
# Отримати список файлів з БД
mysql myapp -e "SELECT file_path FROM media ORDER BY file_path" > db_files.txt
# Порівняти з диском
find /uploads -type f -printf "%P\n" | sort > disk_files.txt
comm -23 disk_files.txt db_files.txt | head -50
Копіювання через rsync
# Первинна синхронізація (можна запускати кілька разів)
rsync -avz --progress \
--checksum \
user@old-server:/var/www/uploads/ \
/var/www/new-site/uploads/
# З виключенням ненужних директорій
rsync -avz \
--exclude='.git' \
--exclude='cache/' \
--exclude='tmp/' \
user@old-server:/var/www/uploads/ \
/var/www/new-site/uploads/
# Delta-синхронізація перед фінальним перемиканням
rsync -avz --delete \
user@old-server:/var/www/uploads/ \
/var/www/new-site/uploads/
--checksum — порівнювати за контрольною сумою, не тільки за часом зміни.
--delete — видаляти файли, які відсутні на джерелі (для фінальної синхронізації).
Завантаження на S3
# Завантаження з одночасністю
aws s3 sync /var/www/uploads/ \
s3://company-media-bucket/uploads/ \
--storage-class STANDARD \
--exclude "*.tmp" \
--exclude "cache/*" \
--acl public-read
# Перевірити кількість завантажених файлів
aws s3 ls s3://company-media-bucket/uploads/ --recursive | wc -l
Оновлення URL у контенті
Після переносу файлів посилання у контенті вказують на старі URL. Потрібно замінити всі входження:
import re
import mysql.connector
def update_media_urls_in_content(db_conn, old_base, new_base):
cursor = db_conn.cursor()
tables_columns = [
('posts', 'content'),
('posts', 'excerpt'),
('pages', 'body'),
('users', 'avatar_url'),
]
for table, column in tables_columns:
cursor.execute(f"SELECT id, {column} FROM {table} WHERE {column} LIKE %s",
(f'%{old_base}%',))
rows = cursor.fetchall()
for row_id, content in rows:
if content:
new_content = content.replace(old_base, new_base)
cursor.execute(
f"UPDATE {table} SET {column} = %s WHERE id = %s",
(new_content, row_id)
)
db_conn.commit()
update_media_urls_in_content(
db_conn,
'https://old-site.com/wp-content/uploads',
'https://new-site.com/uploads'
)
Для WordPress: плагін Better Search Replace або SQL:
UPDATE wp_posts
SET post_content = REPLACE(post_content,
'https://old-site.com/wp-content/uploads/',
'https://cdn.new-site.com/uploads/')
WHERE post_content LIKE '%old-site.com/wp-content/uploads/%';
UPDATE wp_postmeta
SET meta_value = REPLACE(meta_value,
'https://old-site.com',
'https://new-site.com')
WHERE meta_value LIKE '%old-site.com%';
Оптимізація при переносі
Міграція — хороший час для оптимізації зображень:
from PIL import Image
import os
def optimize_images(source_dir, target_dir):
for root, dirs, files in os.walk(source_dir):
for filename in files:
if not filename.lower().endswith(('.jpg', '.jpeg', '.png')):
continue
source_path = os.path.join(root, filename)
rel_path = os.path.relpath(source_path, source_dir)
target_path = os.path.join(target_dir, rel_path)
os.makedirs(os.path.dirname(target_path), exist_ok=True)
img = Image.open(source_path)
# Конвертувати в WebP
webp_path = os.path.splitext(target_path)[0] + '.webp'
img.save(webp_path, 'WEBP', quality=85, method=6)
# Зберегти оригінал з оптимізацією
img.save(target_path, optimize=True, quality=85)
Верифікація після переносу
import requests
import hashlib
def verify_file_integrity(source_server, dest_server, file_list):
errors = []
for path in file_list:
src = requests.get(f"{source_server}/{path}")
dst = requests.get(f"{dest_server}/{path}")
if src.status_code != 200:
errors.append(f"Source missing: {path}")
continue
if dst.status_code != 200:
errors.append(f"Destination missing: {path}")
continue
src_hash = hashlib.md5(src.content).hexdigest()
dst_hash = hashlib.md5(dst.content).hexdigest()
if src_hash != dst_hash:
errors.append(f"Checksum mismatch: {path}")
return errors
Редиректи 301 для старих медіа URL
Якщо URL медіафайлів змінилися, налаштуйте nginx:
# Редирект старих шляхів WP
location ~* ^/wp-content/uploads/(.*)$ {
return 301 /uploads/$1;
}
# Або через map для індивідуальних редиректів
map $uri $media_redirect {
/wp-content/uploads/2022/01/image.jpg /uploads/2022/01/image.webp;
}
Тривалість виконання
Перенос медіафайлів з оновленням посилань у контенті для сайту обсягом до 10GB — 2–3 робочих дні.







