头图

从Chevereto图床迁移至兰空图床

✨ AI摘要:本教程详细介绍了如何将Chevereto图床迁移至兰空图床,主要针对本地存储类型。迁移步骤包括安装兰空图床、备份旧站点数据、配置数据库连接、编写Python脚本进行数据迁移、上传图片文件夹以及设置文件权限。最后,提供了Nginx重定向配置,以便保留旧站点地址并自动重定向到新站点。整个过程确保数据完整性和访问顺畅。

Powered by SparkU AI Platform

本教程针对的图片存储类型主要为本地存储

Chevereto版本为Free 1.3.0版本

兰空图床版本为开源版2.1版本

前期准备

  1. 在新站点地址上安装好兰空图床程序
  2. 备份旧站点(Chevereto)的数据库和网站目录并下载到本地
  3. 准保好新旧站点的MySQL数据库连接信息
  4. 电脑安装好Python环境(推荐3.11版本)并使用pip安装pymysql

    pip install pymysql

迁移过程

  1. 解压备份下载好的Chevereto程序,找到里面的images文件夹,在与images文件夹同级的目录下创建一个Python脚本文件app.py

    内容如下,请阅读注释并修改对应的内容

    import os
    import hashlib
    import pymysql
    from datetime import datetime
    
    # ===== 数据库配置 =====
    CHEVERETO_DB = {
        "host": "旧数据库地址",
        "user": "数据库用户名",
        "password": "数据库密码",
        "database": "数据库",
        "charset": "utf8mb4"
    }
    
    LSKY_DB = {
        "host": "新数据库地址",
        "user": "数据库用户名",
        "password": "数据库密码",
        "database": "数据库",
        "charset": "utf8mb4"
    }
    
    CHEVERETO_STORAGE_PATH = "images"  # 本地存储路径
    
    # ===== 文件哈希 =====
    def calc_hash(file_path):
        md5_hash = hashlib.md5()
        sha1_hash = hashlib.sha1()
        try:
            with open(file_path, "rb") as f:
                for chunk in iter(lambda: f.read(8192), b""):
                    md5_hash.update(chunk)
                    sha1_hash.update(chunk)
            return md5_hash.hexdigest(), sha1_hash.hexdigest()
        except FileNotFoundError:
            print(f"[WARN] 文件不存在: {file_path}")
            return None, None
    
    EXT_MIME_MAP = {
        "jpg": "image/jpeg",
        "jpeg": "image/jpeg",
        "png": "image/png",
        "gif": "image/gif",
        "webp": "image/webp"
    }
    
    def get_mimetype(ext):
        return EXT_MIME_MAP.get(ext.lower(), f"image/{ext.lower()}")
    
    # ===== 更新专辑图片数量(避免负数) =====
    def update_album_image_num(lsky_cur, album_id, delta):
        if delta < 0:
            lsky_cur.execute(
                "UPDATE albums SET image_num = GREATEST(image_num + %s, 0) WHERE id = %s",
                (delta, album_id)
            )
        else:
            lsky_cur.execute(
                "UPDATE albums SET image_num = image_num + %s WHERE id = %s",
                (delta, album_id)
            )
    
    # ===== 迁移逻辑 =====
    def migrate():
        che_conn = pymysql.connect(**CHEVERETO_DB)
        lsky_conn = pymysql.connect(**LSKY_DB)
    
        che_cur = che_conn.cursor(pymysql.cursors.DictCursor)
        lsky_cur = lsky_conn.cursor()
    
        che_cur.execute("SELECT * FROM chv_images")
        rows = che_cur.fetchall()
    
        insert_sql = """
        INSERT INTO images (
            user_id, album_id, group_id, strategy_id,
            `key`, `path`, `name`, origin_name, alias_name,
            size, mimetype, extension, md5, sha1,
            width, height, permission, is_unhealthy,
            uploaded_ip, created_at, updated_at
        ) VALUES (
            %s, %s, %s, %s,
            %s, %s, %s, %s, %s,
            %s, %s, %s, %s, %s,
            %s, %s, %s, %s,
            %s, %s, %s
        )
        """
    
        log_file = open("migrate_error.log", "w", encoding="utf-8")
    
        for row in rows:
            date_path = row["image_date"].strftime("%Y/%m/%d")
            filename = f"{row['image_name']}.{row['image_extension']}"
            file_path = os.path.join(CHEVERETO_STORAGE_PATH, date_path, filename)
    
            md5, sha1 = calc_hash(file_path)
            if not md5:
                log_file.write(f"缺失文件: {file_path}\n")
                continue
    
            mimetype = get_mimetype(row["image_extension"])
            key = row["image_name"]
    
            created_updated_time = row["image_date"].replace(hour=0, minute=0, second=0, microsecond=0)
            date_str = created_updated_time.strftime("%Y-%m-%d %H:%M:%S")
    
            data = (
                1, 1, 1, 1, # 这里的四个数字对应着四个ID,分别是user_id、album_id、group_id和strategy_id,请根据实际情况进行调整,如果不知道怎么获取请看兰空图床后台中的接口部分的说明
                key, date_path, filename,
                row["image_original_filename"],
                row["image_title"] or "",
                round(row["image_size"]/1024, 2),
                mimetype, row["image_extension"],
                md5, sha1,
                row["image_width"], row["image_height"],
                0, 0,
                "0.0.0.0", date_str, date_str # 这里是默认上传IP地址,如有需要请自行替换
            )
    
    s        try:
                lsky_cur.execute(insert_sql, data)
                update_album_image_num(lsky_cur, 1, 1)  # 成功添加时增加数量
            except pymysql.err.IntegrityError as e:
                if "Duplicate entry" in str(e):
                    new_key = f"{row['image_name']}_{row['image_id']}"
                    data = list(data)
                    data[4] = new_key
                    try:
                        lsky_cur.execute(insert_sql, tuple(data))
                        update_album_image_num(lsky_cur, 1, 1)
                    except Exception as e2:
                        log_file.write(f"再次插入失败 {filename}: {e2}\n")
                else:
                    log_file.write(f"插入失败 {filename}: {e}\n")
            except Exception as e:
                log_file.write(f"插入失败 {filename}: {e}\n")
    
        lsky_conn.commit()
        print("[OK] 迁移完成")
    
        log_file.close()
        che_cur.close()
        lsky_cur.close()
        che_conn.close()
        lsky_conn.close()
    
    if __name__ == "__main__":
        migrate()
    
  2. 然后运行该脚本进行数据迁移

    python app.py
  3. 然后将Chevereto里面的images的文件夹内的所有年份文件夹上传到新站点的/storage/app/uploads
  4. 最后请记住将这里面所有年份的文件夹和子文件夹权限设置为和程序主题一致的权限,例如755/www
  5. 迁移完成

重定向

如果你还想保留旧的站点地址但是想重定向到新站点的地址,若您使用Nginx作为Web服务,在旧站点中的重定向配置文件部分使用下面的配置:

location ~ ^/images/(.*)$ {
  return 301 https://新站点地址/i/$1;
}

location / {
  return 301 https://新站点地址;
}

访问旧站点的图片时会被301自动永久重定向到新站点的地址和路径上

至此完整的迁移流程已经结束

从Chevereto图床迁移至兰空图床
https://www.kindyear.cn/archives/998/
本文作者 KINDYEAR
发布时间 2025-09-27
许可协议 CC BY-NC-SA 4.0
发表新评论